From c93da1bb9a5573e30d872b2c5c46fa11b39727b7 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 9 Oct 2025 15:00:31 +0200 Subject: [PATCH 1/2] Add ensureApplied to the quotes reflect API --- .../quoted/runtime/impl/QuotesImpl.scala | 2 ++ library/src/scala/quoted/Quotes.scala | 3 +++ project/MiMaFilters.scala | 1 + tests/pos-macros/i23969/Macro.scala | 26 +++++++++++++++++++ tests/pos-macros/i23969/Main.scala | 1 + 5 files changed, 33 insertions(+) create mode 100644 tests/pos-macros/i23969/Macro.scala create mode 100644 tests/pos-macros/i23969/Main.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 62080c41719a..10843c824283 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -484,6 +484,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler argss.foldLeft(self: Term)(Apply(_, _)) def appliedToNone: Apply = self.appliedToArgs(Nil) + def ensureApplied: Term = + if (self.tpe.widen.isParameterless) self else self.appliedToNone def appliedToType(targ: TypeRepr): Term = self.appliedToTypes(targ :: Nil) def appliedToTypes(targs: List[TypeRepr]): Term = diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index a00c620788c9..a31d26cb3df4 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -890,6 +890,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** The current tree applied to (): `tree()` */ def appliedToNone: Apply + /** The current tree applied to `()` unless the tree's widened type is parameterless */ + def ensureApplied: Term + /** The current tree applied to given type argument: `tree[targ]` */ def appliedToType(targ: TypeRepr): Term diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index bc842ae86039..0a7ac93d22c8 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -140,6 +140,7 @@ object MiMaFilters { ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ValDefModule.let"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newClass"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newModule"), + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TermMethods.ensureApplied"), // Change `experimental` annotation to a final class ProblemFilters.exclude[FinalClassProblem]("scala.annotation.experimental"), diff --git a/tests/pos-macros/i23969/Macro.scala b/tests/pos-macros/i23969/Macro.scala new file mode 100644 index 000000000000..e659810b278f --- /dev/null +++ b/tests/pos-macros/i23969/Macro.scala @@ -0,0 +1,26 @@ + +import scala.quoted._ + +object TestMethods: + def a1 = () + def a2() = () + +transparent inline def runMacro() = ${runMacroImpl} +def runMacroImpl(using Quotes): Expr[Any] = + import quotes.reflect._ + + // ensureApplied test + Select.unique('{TestMethods}.asTerm, "a1").ensureApplied match + case Select(_, _) => + case _ => assert(false) + Select.unique('{TestMethods}.asTerm, "a2").ensureApplied match + case Apply(_, _) => + case _ => assert(false) + + // regression test + val Inlined(_, _, generated) = '{BigDecimal(0).toString()}.asTerm : @unchecked + val Inlined(_, _, bigDecimal) = '{BigDecimal(0)}.asTerm : @unchecked + val custom = Select.unique(bigDecimal, "toString").ensureApplied + // ensure both have the same shape + assert(custom.toString == generated.toString) + custom.asExpr diff --git a/tests/pos-macros/i23969/Main.scala b/tests/pos-macros/i23969/Main.scala new file mode 100644 index 000000000000..455b473d0157 --- /dev/null +++ b/tests/pos-macros/i23969/Main.scala @@ -0,0 +1 @@ +@main def Test() = runMacro() From 53f6562b2b2a99a848def478710d0558b4935880 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 10 Oct 2025 14:38:04 +0200 Subject: [PATCH 2/2] Strip unnecessary stack trace from a test --- tests/neg-macros/i23008.check | 7 ------- tests/neg-macros/i23008/Macro_1.scala | 7 ++++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/neg-macros/i23008.check b/tests/neg-macros/i23008.check index f69edff330a0..3d342ef797d7 100644 --- a/tests/neg-macros/i23008.check +++ b/tests/neg-macros/i23008.check @@ -3,13 +3,6 @@ | ^^^^^^^^^^^^^^^^^^ | Exception occurred while executing macro expansion. | java.lang.IllegalArgumentException: requirement failed: value of StringConstant cannot be `null` - | at scala.Predef$.require(Predef.scala:393) - | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2542) - | at scala.quoted.runtime.impl.QuotesImpl$reflect$StringConstant$.apply(QuotesImpl.scala:2541) - | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:82) - | at scala.quoted.ToExpr$StringToExpr.apply(ToExpr.scala:80) - | at scala.quoted.Expr$.apply(Expr.scala:72) - | at Macros$.buildStringCode(Macro_1.scala:9) | |--------------------------------------------------------------------------------------------------------------------- |Inline stack trace diff --git a/tests/neg-macros/i23008/Macro_1.scala b/tests/neg-macros/i23008/Macro_1.scala index 67bd091cd9f3..ef3e00cc08ba 100644 --- a/tests/neg-macros/i23008/Macro_1.scala +++ b/tests/neg-macros/i23008/Macro_1.scala @@ -6,6 +6,11 @@ object Macros { def buildStringCode(using Quotes): Expr[String] = { import quotes.reflect.* val str: String = null - Expr(str) + try + Expr(str) + catch + case error: java.lang.IllegalArgumentException => + error.setStackTrace(Array[StackTraceElement]()) + throw error } }