From 09061ce435813aa1c0652a5de6268c480fab0cd0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 5 Dec 2023 16:57:03 +0100 Subject: [PATCH] Improve `asExprOf` cast error formatting (#19195) 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) ... ``` Example from #19191 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:...) ... ``` [Cherry-picked 7480582f0c539794c42d2c8b5a28384608d4ec16] --- .../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 76b4f3685e6a..84b333b33b5f 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -22,6 +22,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 { @@ -68,11 +69,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