From 2fc7aaea57bf7ac7cac8e92a50d3cfa71e5129dc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 5 Dec 2023 09:33:25 +0100 Subject: [PATCH] Improve `asExprOf` cast error formatting The intention of this change is to make it simpler to read the error message. List the expected type and actual type before the expression. This is usually the most important information and simpler to parse as the expression can get quite long. For the expected type, the actual type and the expression, we print the value in the same line if its String representation has only one line. Otherwise we print it in the next line with an indentation and extra new line at the end. Before: ``` java.lang.Exception: Expr cast exception: ((a: scala.Int) => ({ val v: scala.Int = a Binding.apply[scala.Unit](()) }: Binding[scala.Unit])) of type: scala.Function1[scala.Int, scala.Unit] did not conform to type: scala.Function1[scala.Int, Binding[scala.Unit]] at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:76) ... ``` After: ``` scala.quoted.runtime.impl.ExprCastException: Expected type: scala.Function1[scala.Int, scala.Unit] Actual type: scala.Function1[scala.Int, Binding[scala.Unit]] Expression: ((a: scala.Int) => ({ val v: scala.Int = a Binding.apply[scala.Unit](()) }: Binding[scala.Unit])) at scala.quoted.runtime.impl.QuotesImpl.asExprOf(QuotesImpl.scala:...) ... ``` --- .../runtime/impl/ExprCastException.scala | 20 +++++++++++++++++++ .../quoted/runtime/impl/QuotesImpl.scala | 10 +++++----- 2 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 compiler/src/scala/quoted/runtime/impl/ExprCastException.scala diff --git a/compiler/src/scala/quoted/runtime/impl/ExprCastException.scala b/compiler/src/scala/quoted/runtime/impl/ExprCastException.scala new file mode 100644 index 000000000000..ba870808cee3 --- /dev/null +++ b/compiler/src/scala/quoted/runtime/impl/ExprCastException.scala @@ -0,0 +1,20 @@ +package scala.quoted.runtime.impl + +import dotty.tools.dotc.ast.tpd.Tree +import dotty.tools.dotc.core.Contexts.* + +class ExprCastException(msg: String) extends Exception(msg) + + +object ExprCastException: + def apply(expectedType: String, actualType: String, exprCode: String): ExprCastException = + new ExprCastException( + s"""| + | Expected type: ${formatLines(expectedType)} + | Actual type: ${formatLines(actualType)} + | Expression: ${formatLines(exprCode)} + |""".stripMargin) + + private def formatLines(str: String): String = + if !str.contains("\n") then str + else str.linesIterator.mkString("\n ", "\n ", "\n") diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 04472647b9fc..6aa279129f2b 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -23,6 +23,7 @@ import scala.quoted.runtime.{QuoteUnpickler, QuoteMatching} import scala.quoted.runtime.impl.printers.* import scala.reflect.TypeTest +import dotty.tools.dotc.core.NameKinds.ExceptionBinderName object QuotesImpl { @@ -70,11 +71,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if self.isExprOf[X] then self.asInstanceOf[scala.quoted.Expr[X]] else - throw Exception( - s"""Expr cast exception: ${self.show} - |of type: ${reflect.Printer.TypeReprCode.show(reflect.asTerm(self).tpe)} - |did not conform to type: ${reflect.Printer.TypeReprCode.show(reflect.TypeRepr.of[X])} - |""".stripMargin + throw ExprCastException( + expectedType = reflect.Printer.TypeReprCode.show(reflect.TypeRepr.of[X]), + actualType = reflect.Printer.TypeReprCode.show(reflect.asTerm(self).tpe), + exprCode = self.show ) } end extension