diff --git a/library/src/scala/internal/quoted/showName.scala b/library/src/scala/internal/quoted/showName.scala new file mode 100644 index 000000000000..af992823f5cc --- /dev/null +++ b/library/src/scala/internal/quoted/showName.scala @@ -0,0 +1,24 @@ +package scala.internal.quoted + +/** Annotation used inside a quote to give a custom name to a definition. + * The `name` argument must be a literal String. + * + * Usage: + * ```scala + * def let(name: String)(value: Expr[Int])(in: Expr[Int] => Expr[Int]): Expr[Int] = '{ + * @showName(${Expr(name)}) + * val x = $value + * ${ in('x) } + * } + * ``` + * then using it in + * ```scala + * let("myVal")('{4})(x => '{ $x + 1}).show + * ``` + * will retuns the code + * ```scala + * val myVal = 4 + * myVal + 1 + * ``` + */ +class showName(name: String) extends scala.annotation.Annotation diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 760e90779710..1c3ecf3ef832 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -679,7 +679,8 @@ trait Printers if (vdef.symbol.flags.is(Flags.Mutable)) this += highlightKeyword("var ") else this += highlightKeyword("val ") - this += highlightValDef(name) += ": " + val name1 = splicedName(vdef.symbol).getOrElse(name) + this += highlightValDef(name1) += ": " printTypeTree(tpt) rhs match { case Some(tree) => @@ -714,7 +715,8 @@ trait Printers printProtectedOrPrivate(ddef) - this += highlightKeyword("def ") += highlightValDef((if (isConstructor) "this" else name)) + val name1: String = if (isConstructor) "this" else splicedName(ddef.symbol).getOrElse(name) + this += highlightKeyword("def ") += highlightValDef(name1) printTargsDefs(targs.zip(targs)) val it = argss.iterator while (it.hasNext) @@ -734,8 +736,11 @@ trait Printers case Ident("_") => this += "_" - case IsTerm(tree @ Ident(_)) => - printType(tree.tpe) + case IsIdent(tree) => + splicedName(tree.symbol) match { + case Some(name) => this += name + case _ => printType(tree.tpe) + } case Select(qual, name) => printQualTree(qual) @@ -1637,12 +1642,15 @@ trait Printers def printAnnotation(annot: Term)(given elideThis: Option[Symbol]): Buffer = { val Annotation(ref, args) = annot - this += "@" - printTypeTree(ref) - if (args.isEmpty) - this - else - inParens(printTrees(args, ", ")) + if (annot.symbol.owner.fullName == "scala.internal.quoted.showName") this + else { + this += "@" + printTypeTree(ref) + if (args.isEmpty) + this + else + inParens(printTrees(args, ", ")) + } } def printDefAnnotations(definition: Definition)(given elideThis: Option[Symbol]): Buffer = { @@ -1809,6 +1817,14 @@ trait Printers private def escapedString(str: String): String = str flatMap escapedChar } + private def splicedName(sym: Symbol)(given ctx: Context): Option[String] = { + sym.annots.find(_.symbol.owner.fullName == "scala.internal.quoted.showName").flatMap { + case Apply(_, Literal(Constant(c: String)) :: Nil) => Some(c) + case Apply(_, Inlined(_, _, Literal(Constant(c: String))) :: Nil) => Some(c) + case annot => None + } + } + private object SpecialOp { def unapply(arg: Tree)(given ctx: Context): Option[(String, List[Term])] = arg match { case IsTerm(arg @ Apply(fn, args)) => diff --git a/tests/run-staging/quoted-show-name.check b/tests/run-staging/quoted-show-name.check new file mode 100644 index 000000000000..ae3f5e6928af --- /dev/null +++ b/tests/run-staging/quoted-show-name.check @@ -0,0 +1,13 @@ +((x1: scala.Double) => x1.*({ + val x2: scala.Double = x1.*(x1) + val x4: scala.Double = x2.*(x2) + x4.*({ + val x8: scala.Double = x4.*(x4) + x8.*({ + val x16: scala.Double = x8.*(x8) + val x32: scala.Double = x16.*(x16) + val x64: scala.Double = x32.*(x32) + x64 + }) + }) +})) diff --git a/tests/run-staging/quoted-show-name.scala b/tests/run-staging/quoted-show-name.scala new file mode 100644 index 000000000000..42a42872b5e6 --- /dev/null +++ b/tests/run-staging/quoted-show-name.scala @@ -0,0 +1,21 @@ +import scala.quoted._ +import scala.internal.quoted.showName +import scala.quoted.staging._ +import scala.reflect.ClassTag + +object Test { + given Toolbox = Toolbox.make(getClass.getClassLoader) + def main(args: Array[String]): Unit = withQuoteContext { + println(powerCode(77).show) + } + + def powerCode(n: Long)(given QuoteContext): Expr[Double => Double] = + '{ x1 => ${powerCode(n, 2, 'x1)} } + + def powerCode(n: Long, idx: Int, x: Expr[Double])(given QuoteContext): Expr[Double] = + if (n == 0) '{1.0} + else if (n == 1) x + else if (n % 2 == 0) '{ @showName(${Expr("x" + idx)}) val y = $x * $x; ${powerCode(n / 2, idx * 2, '{y})} } + else '{ $x * ${powerCode(n - 1, idx, x)} } + +}