From d262e9904aa2d5203aa348de39d4eb3f94d1add0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 8 May 2020 16:42:53 +0200 Subject: [PATCH] Move errors and warnings out of QuoteContext --- .../dotty/tools/dotc/transform/Splicer.scala | 4 +-- docs/docs/reference/metaprogramming/macros.md | 4 +-- .../metaprogramming/tasty-reflect.md | 4 +-- .../dotty/internal/CompileTimeMacros.scala | 2 +- .../dotty/internal/StringContextMacro.scala | 4 +-- .../src-bootstrapped/scala/quoted/Expr.scala | 2 +- .../scala/quoted/QuoteContext.scala | 31 +++------------- library/src/scala/quoted/Reporting.scala | 35 +++++++++++++++++++ .../src/scala/quoted/StopQuotedContext.scala | 4 --- tests/neg-macros/quote-error-2/Macro_1.scala | 2 +- tests/neg-macros/quote-error/Macro_1.scala | 2 +- tests/neg-staging/i5941/macro_1.scala | 2 +- .../f-interpolation-1/FQuote_1.scala | 2 +- tests/run-macros/i5941/macro_1.scala | 12 +++---- tests/run-macros/i8671/Macro_1.scala | 4 +-- .../refined-selectable-macro/Macro_1.scala | 8 ++--- .../string-context-implicits/Macro_1.scala | 4 +-- .../tasty-interpolation-1/Macro.scala | 7 ++-- .../tasty-macro-const/quoted_1.scala | 4 +-- .../xml-interpolation-1/XmlQuote_1.scala | 2 +- .../xml-interpolation-2/XmlQuote_1.scala | 6 ++-- .../staged-tuples/StagedTuple.scala | 2 +- 22 files changed, 79 insertions(+), 68 deletions(-) create mode 100644 library/src/scala/quoted/Reporting.scala delete mode 100644 library/src/scala/quoted/StopQuotedContext.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2e535d58d0e3..6d6120612fe0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -52,7 +52,7 @@ object Splicer { catch { case ex: CompilationUnit.SuspendException => throw ex - case ex: scala.quoted.StopQuotedContext if ctx.reporter.hasErrors => + case ex: scala.quoted.Reporting.StopQuotedContext if ctx.reporter.hasErrors => // errors have been emitted EmptyTree case ex: StopInterpretation => @@ -389,7 +389,7 @@ object Splicer { throw new StopInterpretation(sw.toString, pos) case ex: InvocationTargetException => ex.getTargetException match { - case ex: scala.quoted.StopQuotedContext => + case ex: scala.quoted.Reporting.StopQuotedContext => throw ex case MissingClassDefinedInCurrentRun(sym) => if (ctx.settings.XprintSuspension.value) diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index e606b41d06c6..a9f638d025d4 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -727,14 +727,14 @@ private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using val showTp = '[Show[$tp]] Expr.summon(using showTp) match { case Some(showExpr) => '{ $showExpr.show($arg) } - case None => qctx.error(s"could not find implicit for ${showTp.show}", arg); '{???} + case None => Reporting.error(s"could not find implicit for ${showTp.show}", arg); '{???} } } val newArgsExpr = Varargs(argShowedExprs) '{ $sc.s($newArgsExpr: _*) } case _ => // `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."` - qctx.error(s"Args must be explicit", argsExpr) + Reporting.error(s"Args must be explicit", argsExpr) '{???} } } diff --git a/docs/docs/reference/metaprogramming/tasty-reflect.md b/docs/docs/reference/metaprogramming/tasty-reflect.md index b5ac80194972..3e576d140afa 100644 --- a/docs/docs/reference/metaprogramming/tasty-reflect.md +++ b/docs/docs/reference/metaprogramming/tasty-reflect.md @@ -46,13 +46,13 @@ def natConstImpl(x: Expr[Int])(using qctx: QuoteContext): Expr[Int] = { xTree match { case Inlined(_, _, Literal(Constant(n: Int))) => if (n <= 0) { - qctx.error("Parameter must be natural number") + Reporting.error("Parameter must be natural number") '{0} } else { xTree.seal.cast[Int] } case _ => - qctx.error("Parameter must be a known constant") + Reporting.error("Parameter must be a known constant") '{0} } } diff --git a/library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala b/library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala index 4af284097d93..f30a53a23c24 100644 --- a/library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala +++ b/library/src-bootstrapped/dotty/internal/CompileTimeMacros.scala @@ -8,4 +8,4 @@ object CompileTimeMacros: case (Expr.StringContext(Consts(parts)), Varargs(args2)) => Expr(StringContext(parts: _*).s(args2.map(_.show): _*)) case _ => - qctx.throwError("compiletime.code must be used as a string interpolator `code\"...\"`") + Reporting.throwError("compiletime.code must be used as a string interpolator `code\"...\"`") diff --git a/library/src-bootstrapped/dotty/internal/StringContextMacro.scala b/library/src-bootstrapped/dotty/internal/StringContextMacro.scala index 4446cabc613d..5b7a06877b9a 100644 --- a/library/src-bootstrapped/dotty/internal/StringContextMacro.scala +++ b/library/src-bootstrapped/dotty/internal/StringContextMacro.scala @@ -63,12 +63,12 @@ object StringContextMacro { val (partsExpr, parts) = strCtxExpr match { case Expr.StringContext(p1 @ Consts(p2)) => (p1.toList, p2.toList) - case _ => qctx.throwError("Expected statically known String Context", strCtxExpr) + case _ => Reporting.throwError("Expected statically known String Context", strCtxExpr) } val args = argsExpr match { case Varargs(args) => args - case _ => qctx.throwError("Expected statically known argument list", argsExpr) + case _ => Reporting.throwError("Expected statically known argument list", argsExpr) } val reporter = new Reporter{ diff --git a/library/src-bootstrapped/scala/quoted/Expr.scala b/library/src-bootstrapped/scala/quoted/Expr.scala index 1d823512d1b2..55d31d461979 100644 --- a/library/src-bootstrapped/scala/quoted/Expr.scala +++ b/library/src-bootstrapped/scala/quoted/Expr.scala @@ -43,7 +43,7 @@ class Expr[+T] private[scala] { * Otherwise returns the value. */ final def unliftOrError[U >: T](using qctx: QuoteContext, unlift: Unliftable[U]): U = - unlift(this).getOrElse(qctx.throwError(s"Expected a known value. \n\nThe value of: $show\ncould not be unlifted using $unlift", this)) + unlift(this).getOrElse(Reporting.throwError(s"Expected a known value. \n\nThe value of: $show\ncould not be unlifted using $unlift", this)) /** Pattern matches `this` against `that`. Effectively performing a deep equality check. * It does the equivalent of diff --git a/library/src-bootstrapped/scala/quoted/QuoteContext.scala b/library/src-bootstrapped/scala/quoted/QuoteContext.scala index 046294257ae4..7ec7a0d9442f 100644 --- a/library/src-bootstrapped/scala/quoted/QuoteContext.scala +++ b/library/src-bootstrapped/scala/quoted/QuoteContext.scala @@ -34,31 +34,10 @@ trait QuoteContext { self => val tasty: self.tasty.type } - /** Report an error at the position of the macro expansion */ - def error(msg: => String): Unit = - tasty.error(msg, tasty.rootPosition) - - /** Report an error at the on the position of `expr` */ - def error(msg: => String, expr: Expr[Any]): Unit = - tasty.error(msg, expr.unseal(using this).pos) - - /** Report an error at the position of the macro expansion and throws a StopQuotedContext */ - def throwError(msg: => String): Nothing = { - error(msg) - throw new StopQuotedContext - } - /** Report an error at the on the position of `expr` and throws a StopQuotedContext */ - def throwError(msg: => String, expr: Expr[Any]): Nothing = { - error(msg, expr) - throw new StopQuotedContext - } - - /** Report a warning */ - def warning(msg: => String): Unit = - tasty.warning(msg, tasty.rootPosition) - - /** Report a warning at the on the position of `expr` */ - def warning(msg: => String, expr: Expr[Any]): Unit = - tasty.warning(msg, expr.unseal(using this).pos) +} +object QuoteContext { + // TODO remove in 0.26.0 + @deprecated("Errors and warnings have been moved to scala.quoted.Reporting", "0.25.0") + given error_and_warining_on_QuoteContext as Conversion[QuoteContext, Reporting.type] = _ => Reporting } diff --git a/library/src/scala/quoted/Reporting.scala b/library/src/scala/quoted/Reporting.scala new file mode 100644 index 000000000000..82b9254a66e6 --- /dev/null +++ b/library/src/scala/quoted/Reporting.scala @@ -0,0 +1,35 @@ +package scala.quoted + +object Reporting { + + /** Report an error at the position of the macro expansion */ + def error(msg: => String)(using qctx: QuoteContext): Unit = + qctx.tasty.error(msg, qctx.tasty.rootPosition) + + /** Report an error at the on the position of `expr` */ + def error(msg: => String, expr: Expr[Any])(using qctx: QuoteContext): Unit = + qctx.tasty.error(msg, expr.unseal.pos) + + /** Report an error at the position of the macro expansion and throws a StopQuotedContext */ + def throwError(msg: => String)(using qctx: QuoteContext): Nothing = { + error(msg) + throw new StopQuotedContext + } + /** Report an error at the on the position of `expr` and throws a StopQuotedContext */ + def throwError(msg: => String, expr: Expr[Any])(using qctx: QuoteContext): Nothing = { + error(msg, expr) + throw new StopQuotedContext + } + + /** Report a warning */ + def warning(msg: => String)(using qctx: QuoteContext): Unit = + qctx.tasty.warning(msg, qctx.tasty.rootPosition) + + /** Report a warning at the on the position of `expr` */ + def warning(msg: => String, expr: Expr[_])(using qctx: QuoteContext): Unit = + qctx.tasty.warning(msg, expr.unseal.pos) + + /** Throwable used to stop the expansion of a macro after an error was reported */ + class StopQuotedContext extends Throwable + +} diff --git a/library/src/scala/quoted/StopQuotedContext.scala b/library/src/scala/quoted/StopQuotedContext.scala deleted file mode 100644 index 455fe8ee0961..000000000000 --- a/library/src/scala/quoted/StopQuotedContext.scala +++ /dev/null @@ -1,4 +0,0 @@ -package scala.quoted - -/** Stop code generation after an error has been reported */ -class StopQuotedContext extends Throwable diff --git a/tests/neg-macros/quote-error-2/Macro_1.scala b/tests/neg-macros/quote-error-2/Macro_1.scala index 7eccdd28e9ee..4b3ed471f4a1 100644 --- a/tests/neg-macros/quote-error-2/Macro_1.scala +++ b/tests/neg-macros/quote-error-2/Macro_1.scala @@ -7,6 +7,6 @@ object Macro_1 { def msg(b: Boolean)(using qctx: QuoteContext): Expr[String] = if (b) '{"foo(true)"} - else { qctx.error("foo cannot be called with false"); '{ ??? } } + else { Reporting.error("foo cannot be called with false"); '{ ??? } } } diff --git a/tests/neg-macros/quote-error/Macro_1.scala b/tests/neg-macros/quote-error/Macro_1.scala index cbe58acc3385..a9a605fc9d6b 100644 --- a/tests/neg-macros/quote-error/Macro_1.scala +++ b/tests/neg-macros/quote-error/Macro_1.scala @@ -4,5 +4,5 @@ object Macro_1 { inline def foo(inline b: Boolean): Unit = ${fooImpl('b)} def fooImpl(b: Expr[Boolean])(using qctx: QuoteContext) : Expr[Unit] = if (b.unliftOrError) '{println("foo(true)")} - else { qctx.error("foo cannot be called with false"); '{ ??? } } + else { Reporting.error("foo cannot be called with false"); '{ ??? } } } diff --git a/tests/neg-staging/i5941/macro_1.scala b/tests/neg-staging/i5941/macro_1.scala index 0fad7b7543a2..5c32dc801d70 100644 --- a/tests/neg-staging/i5941/macro_1.scala +++ b/tests/neg-staging/i5941/macro_1.scala @@ -33,7 +33,7 @@ object Lens { apply($getter)(setter) } case _ => - qctx.error("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") + Reporting.error("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") '{???} } } diff --git a/tests/run-macros/f-interpolation-1/FQuote_1.scala b/tests/run-macros/f-interpolation-1/FQuote_1.scala index 381a7f0d2cec..42b82f808f58 100644 --- a/tests/run-macros/f-interpolation-1/FQuote_1.scala +++ b/tests/run-macros/f-interpolation-1/FQuote_1.scala @@ -39,7 +39,7 @@ object FQuote { values.forall(isStringConstant) => values.collect { case Literal(Constant(value: String)) => value } case tree => - qctx.error(s"String literal expected, but ${tree.showExtractors} found") + Reporting.error(s"String literal expected, but ${tree.showExtractors} found") return '{???} } diff --git a/tests/run-macros/i5941/macro_1.scala b/tests/run-macros/i5941/macro_1.scala index c587b4c857e8..073302f648ac 100644 --- a/tests/run-macros/i5941/macro_1.scala +++ b/tests/run-macros/i5941/macro_1.scala @@ -53,7 +53,7 @@ object Lens { apply($getter)(setter) } case _ => - qctx.error("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") + Reporting.error("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") '{???} } } @@ -95,7 +95,7 @@ object Iso { // 2. A must be a tuple // 3. The parameters of S must match A if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags.Case)) Some(true) else None).isEmpty) { - qctx.error("Only support generation for case classes") + Reporting.error("Only support generation for case classes") return '{???} } @@ -106,13 +106,13 @@ object Iso { } if (cls.caseFields.size != 1) { - qctx.error("Use GenIso.fields for case classes more than one parameter") + Reporting.error("Use GenIso.fields for case classes more than one parameter") return '{???} } val fieldTp = tpS.memberType(cls.caseFields.head) if (!(fieldTp =:= tpA)) { - qctx.error(s"The type of case class field $fieldTp does not match $tpA") + Reporting.error(s"The type of case class field $fieldTp does not match $tpA") '{???} } else '{ // (p: S) => p._1 @@ -139,7 +139,7 @@ object Iso { val cls = tpS.classSymbol.get if (cls.caseFields.size != 0) { - qctx.error("Use GenIso.fields for case classes more than one parameter") + Reporting.error("Use GenIso.fields for case classes more than one parameter") return '{???} } @@ -154,7 +154,7 @@ object Iso { } } else { - qctx.error("Only support generation for case classes or singleton types") + Reporting.error("Only support generation for case classes or singleton types") '{???} } } diff --git a/tests/run-macros/i8671/Macro_1.scala b/tests/run-macros/i8671/Macro_1.scala index 354ddff20035..8de729f001d8 100644 --- a/tests/run-macros/i8671/Macro_1.scala +++ b/tests/run-macros/i8671/Macro_1.scala @@ -18,11 +18,11 @@ object FileName { case Right(fn) => '{FileName.unsafe(${Expr(fn.name)})} // Or `Expr(fn)` if there is a `Liftable[FileName]` case Left(_) => - qctx.throwError(s"$s is not a valid file name! It must not contain a /", fileName) + Reporting.throwError(s"$s is not a valid file name! It must not contain a /", fileName) } case _ => - qctx.throwError(s"$fileName is not a valid file name. It must be a literal string", fileName) + Reporting.throwError(s"$fileName is not a valid file name. It must be a literal string", fileName) } } diff --git a/tests/run-macros/refined-selectable-macro/Macro_1.scala b/tests/run-macros/refined-selectable-macro/Macro_1.scala index d73004b445f8..f52c68a7761f 100644 --- a/tests/run-macros/refined-selectable-macro/Macro_1.scala +++ b/tests/run-macros/refined-selectable-macro/Macro_1.scala @@ -23,7 +23,7 @@ object Macro { case _: TypeBounds => rec(parent) case _: MethodType | _: PolyType | _: TypeBounds | _: ByNameType => - qctx.warning(s"Ignored `$name` as a field of the record", s) + Reporting.warning(s"Ignored `$name` as a field of the record", s) rec(parent) case info: Type => (name, info) :: rec(parent) @@ -57,10 +57,10 @@ object Macro { // Tuple2(S, T) where S must be a constant string type case AppliedType(parent, ConstantType(Constant(name: String)) :: (info: Type) :: Nil) if (parent.typeSymbol == defn.TupleClass(2)) => if seen(name) then - qctx.error(s"Repeated record name: $name", s) + Reporting.error(s"Repeated record name: $name", s) (seen + name, (name, info)) case _ => - qctx.error("Tuple type was not explicit expected `(S, T)` where S is a singleton string", s) + Reporting.error("Tuple type was not explicit expected `(S, T)` where S is a singleton string", s) (seen, ("", defn.AnyType)) } } @@ -79,7 +79,7 @@ object Macro { })._2 // Tuple case _ => - qctx.error("Tuple type must be of known size", s) + Reporting.error("Tuple type must be of known size", s) Nil } } diff --git a/tests/run-macros/string-context-implicits/Macro_1.scala b/tests/run-macros/string-context-implicits/Macro_1.scala index 47fbafe5845b..976cb948b542 100644 --- a/tests/run-macros/string-context-implicits/Macro_1.scala +++ b/tests/run-macros/string-context-implicits/Macro_1.scala @@ -11,14 +11,14 @@ private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using val showTp = '[Show[$tp]] Expr.summon(using showTp) match { case Some(showExpr) => '{ $showExpr.show($arg) } - case None => qctx.error(s"could not find implicit for ${showTp.show}", arg); '{???} + case None => Reporting.error(s"could not find implicit for ${showTp.show}", arg); '{???} } } val newArgsExpr = Varargs(argShowedExprs) '{ $sc.s($newArgsExpr: _*) } case _ => // `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."` - qctx.error(s"Args must be explicit", argsExpr) + Reporting.error(s"Args must be explicit", argsExpr) '{???} } } diff --git a/tests/run-macros/tasty-interpolation-1/Macro.scala b/tests/run-macros/tasty-interpolation-1/Macro.scala index 091264a30e93..ce480610b10c 100644 --- a/tests/run-macros/tasty-interpolation-1/Macro.scala +++ b/tests/run-macros/tasty-interpolation-1/Macro.scala @@ -2,6 +2,7 @@ import scala.quoted._ import scala.language.implicitConversions import scala.quoted.autolift +import scala.quoted.Reporting.error object Macro { @@ -36,15 +37,15 @@ abstract class MacroStringInterpolator[T] { catch { case ex: NotStaticlyKnownError => // TODO use ex.expr to recover the position - qctx.error(ex.getMessage) + error(ex.getMessage) '{???} case ex: StringContextError => // TODO use ex.idx to recover the position - qctx.error(ex.getMessage) + error(ex.getMessage) '{???} case ex: ArgumentError => // TODO use ex.idx to recover the position - qctx.error(ex.getMessage) + error(ex.getMessage) '{???} } } diff --git a/tests/run-macros/tasty-macro-const/quoted_1.scala b/tests/run-macros/tasty-macro-const/quoted_1.scala index 54479dce41a0..a685480d4fa1 100644 --- a/tests/run-macros/tasty-macro-const/quoted_1.scala +++ b/tests/run-macros/tasty-macro-const/quoted_1.scala @@ -10,13 +10,13 @@ object Macros { xTree match { case Inlined(_, _, Literal(Constant(n: Int))) => if (n <= 0) { - qctx.error("Parameter must be natural number") + Reporting.error("Parameter must be natural number") '{0} } else { xTree.seal.cast[Int] } case _ => - qctx.error("Parameter must be a known constant") + Reporting.error("Parameter must be a known constant") '{0} } } diff --git a/tests/run-macros/xml-interpolation-1/XmlQuote_1.scala b/tests/run-macros/xml-interpolation-1/XmlQuote_1.scala index 25339dd4678f..43489f096ac5 100644 --- a/tests/run-macros/xml-interpolation-1/XmlQuote_1.scala +++ b/tests/run-macros/xml-interpolation-1/XmlQuote_1.scala @@ -49,7 +49,7 @@ object XmlQuote { values.forall(isStringConstant) => values.collect { case Literal(Constant(value: String)) => value } case tree => - qctx.error(s"String literal expected, but ${tree.showExtractors} found") + Reporting.error(s"String literal expected, but ${tree.showExtractors} found") return '{ ??? } } diff --git a/tests/run-macros/xml-interpolation-2/XmlQuote_1.scala b/tests/run-macros/xml-interpolation-2/XmlQuote_1.scala index 3437ee2f9c13..e63ba598ae6b 100644 --- a/tests/run-macros/xml-interpolation-2/XmlQuote_1.scala +++ b/tests/run-macros/xml-interpolation-2/XmlQuote_1.scala @@ -44,15 +44,15 @@ object XmlQuote { values.iterator.map { case Literal(Constant(value: String)) => value case _ => - qctx.error("Expected statically known String") + Reporting.error("Expected statically known String") return '{???} }.toList case _ => - qctx.error("Expected statically known StringContext") + Reporting.error("Expected statically known StringContext") return '{???} } case _ => - qctx.error("Expected statically known SCOps") + Reporting.error("Expected statically known SCOps") return '{???} } diff --git a/tests/run-staging/staged-tuples/StagedTuple.scala b/tests/run-staging/staged-tuples/StagedTuple.scala index d4e0e7999718..6747c29ade94 100644 --- a/tests/run-staging/staged-tuples/StagedTuple.scala +++ b/tests/run-staging/staged-tuples/StagedTuple.scala @@ -131,7 +131,7 @@ object StagedTuple { else { def fallbackApply(): Expr[Elem[Tup, N]] = nValue match { case Some(n) => - qctx.error("index out of bounds: " + n, tup) + Reporting.error("index out of bounds: " + n, tup) '{ throw new IndexOutOfBoundsException(${Expr(n.toString)}) } case None => '{dynamicApply($tup, $n)} }