From 789471d9c38994b43d3efdf858fcfa24296be82f Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 7 Jun 2019 14:56:03 +0200 Subject: [PATCH] Add Dotty-style tree constructor DSL to reflect --- library/src/scala/tasty/reflect/TreeOps.scala | 37 +++++++++++++++ tests/run-macros/reflect-dsl/assert_1.scala | 46 +++++++++++++++++++ tests/run-macros/reflect-dsl/test_2.scala | 29 ++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 tests/run-macros/reflect-dsl/assert_1.scala create mode 100644 tests/run-macros/reflect-dsl/test_2.scala diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index e2c28c98d070..1d3dd5863d60 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -179,6 +179,43 @@ trait TreeOps extends Core { def pos(implicit ctx: Context): Position = kernel.Term_pos(self) def underlyingArgument(implicit ctx: Context): Term = kernel.Term_underlyingArgument(self) def underlying(implicit ctx: Context): Term = kernel.Term_underlying(self) + + /** A unary apply node with given argument: `tree(arg)` */ + def appliedTo(arg: Term)(implicit ctx: Context): Term = + appliedToArgs(arg :: Nil) + + /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */ + def appliedTo(arg: Term, args: Term*)(implicit ctx: Context): Term = + appliedToArgs(arg :: args.toList) + + /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */ + def appliedToArgs(args: List[Term])(implicit ctx: Context): Apply = + Apply(self, args) + + /** The current tree applied to given argument lists: + * `tree (argss(0)) ... (argss(argss.length -1))` + */ + def appliedToArgss(argss: List[List[Term]])(implicit ctx: Context): Term = + ((self: Term) /: argss)(Apply(_, _)) + + /** The current tree applied to (): `tree()` */ + def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) + + /** The current tree applied to given type argument: `tree[targ]` */ + def appliedToType(targ: Type)(implicit ctx: Context): Term = + appliedToTypes(targ :: Nil) + + /** The current tree applied to given type arguments: `tree[targ0, ..., targN]` */ + def appliedToTypes(targs: List[Type])(implicit ctx: Context): Term = + appliedToTypeTrees(targs map (Inferred(_))) + + /** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */ + def appliedToTypeTrees(targs: List[TypeTree])(implicit ctx: Context): Term = + if (targs.isEmpty) self else TypeApply(self, targs) + + /** A select node that selects the given symbol. + */ + def select(sym: Symbol)(implicit ctx: Context): Select = Select(self, sym) } object IsTerm { diff --git a/tests/run-macros/reflect-dsl/assert_1.scala b/tests/run-macros/reflect-dsl/assert_1.scala new file mode 100644 index 000000000000..f1e60b925e0b --- /dev/null +++ b/tests/run-macros/reflect-dsl/assert_1.scala @@ -0,0 +1,46 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ${ assertImpl('condition, '{""}) } + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + import util._ + + def isImplicitMethodType(tp: Type): Boolean = + Type.IsMethodType.unapply(tp).flatMap(tp => if tp.isImplicit then Some(true) else None).nonEmpty + + cond.unseal.underlyingArgument match { + case t @ Apply(sel @ Select(lhs, op), rhs :: Nil) => + let(lhs) { left => + let(rhs) { right => + val app = left.select(sel.symbol).appliedTo(right) + let(app) { result => + val l = left.seal + val r = right.seal + val b = result.seal.cast[Boolean] + val code = '{ scala.Predef.assert($b) } + code.unseal + } + } + }.seal.cast[Unit] + case Apply(f @ Apply(sel @ Select(Apply(qual, lhs :: Nil), op), rhs :: Nil), implicits) + if isImplicitMethodType(f.tpe) => + let(lhs) { left => + let(rhs) { right => + val app = qual.appliedTo(left).select(sel.symbol).appliedTo(right) + let(Apply(app, implicits)) { result => + val l = left.seal + val r = right.seal + val b = result.seal.cast[Boolean] + val code = '{ scala.Predef.assert($b) } + code.unseal + } + } + }.seal.cast[Unit] + } + } + +} diff --git a/tests/run-macros/reflect-dsl/test_2.scala b/tests/run-macros/reflect-dsl/test_2.scala new file mode 100644 index 000000000000..1525360a71f7 --- /dev/null +++ b/tests/run-macros/reflect-dsl/test_2.scala @@ -0,0 +1,29 @@ +object Test { + import scalatest._ + + case class Box[T](v: T) { + def >(that: Box[T]): Boolean = this == that + } + + trait EqInt + implicit val eq: EqInt = new EqInt {} + + implicit class AnyOps[T](x: T) { + def === (y: T)(implicit c: EqInt) = x == y + } + + def main(args: Array[String]): Unit = { + val a = Box(Some(10)) + val five: Float = 5.0f + val six: Double = 6.0 + val ten: Int = 10 + assert(a.v === Some(10)) + assert(five < six) + assert(five > 4) + assert(ten > 5) + assert(six < 7) + assert(six > 5L) + assert(Box(6) > Box(6)) + assert(Box("h") > Box("h")) + } +}