diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index c4fb30fad025..4de77e207d75 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -241,12 +241,22 @@ trait TreeOps extends Core { object Ref { - /** Create a reference tree */ + /** Create a reference tree from a symbol + * + * If `sym` refers to a class member `foo` in class `C`, + * returns a tree representing `C.this.foo`. + * + * If `sym` refers to a local definition `foo`, returns + * a tree representing `foo`. + * + * @note In both cases, the constructed tree should only + * be spliced into the places where such accesses make sense. + * For example, it is incorrect to have `C.this.foo` outside + * the class body of `C`, or have `foo` outside the lexical + * scope for the definition of `foo`. + */ def apply(sym: Symbol)(given ctx: Context): Ref = internal.Ref_apply(sym) - - // TODO def copy(original: Tree)(name: String)(given ctx: Context): Ref - } given (given Context): IsInstanceOf[Ident] = internal.isInstanceOfIdent diff --git a/tests/run-macros/i6988/FirstArg_1.scala b/tests/run-macros/i6988/FirstArg_1.scala new file mode 100644 index 000000000000..c3466d0c1b38 --- /dev/null +++ b/tests/run-macros/i6988/FirstArg_1.scala @@ -0,0 +1,32 @@ +package foo + +case class FirstArg(value: Any, source: String) +object FirstArg { + inline given create: FirstArg = ${Macros.argsImpl} +} + +object Macros { + import scala.quoted._ + + def argsImpl(given qctx: QuoteContext): Expr[FirstArg] = { + import qctx.tasty.{_, given} + + def enclosingClass(cur: Symbol = rootContext.owner): Symbol = + if (cur.isClassDef) cur + else enclosingClass(cur.owner) + + def enclosingParamList(owner: Symbol): Seq[Seq[Symbol]] = + if owner.isClassDef then + owner.tree match + case tdef: ClassDef => + tdef.constructor.paramss map { _ map {_.symbol }} + else enclosingParamList(owner.owner) + + def literal(value: String): Expr[String] = + Literal(Constant(value)).seal.asInstanceOf[Expr[String]] + val paramss = enclosingParamList(rootContext.owner) + val firstArg = paramss.flatten.head + val ref = Select.unique(This(enclosingClass()), firstArg.name) + '{ FirstArg(${ref.seal}, ${Expr(firstArg.name)}) } + } +} diff --git a/tests/run-macros/i6988/Test_2.scala b/tests/run-macros/i6988/Test_2.scala new file mode 100644 index 000000000000..0cd8e65d82ee --- /dev/null +++ b/tests/run-macros/i6988/Test_2.scala @@ -0,0 +1,16 @@ +object Test { + var firstArgName = "" + var firstArgValue: Any = "" + def main(args: Array[String]): Unit = { + val x = new Foo("something", 2L, false) + assert("p1" == firstArgName) + assert("something" == firstArgValue) + } + def debug(given foo.FirstArg): Unit = { + firstArgName = summon[foo.FirstArg].source + firstArgValue = summon[foo.FirstArg].value + } + class Foo(p1: String, p2: Long, p3: Boolean) { + debug + } +}