diff --git a/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala b/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala index cfc09a8ed836..32c978073194 100644 --- a/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala +++ b/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala @@ -45,8 +45,14 @@ object FromSymbol { case tpd.EmptyTree => tpd.DefDef(sym) } - def valDefFromSym(sym: TermSymbol)(using Context): tpd.ValDef = sym.defTree match { + def valDefFromSym(sym: TermSymbol)(using Context): tpd.ValOrDefDef = sym.defTree match { case tree: tpd.ValDef => tree + case tree: tpd.DefDef => + // `Getters` phase replaces val class members with defs, + // so we may see a defdef here if we are running this on a symbol compiled + // in the same compilation (but before suspension, so that + // the symbol could have reached `Getters`). + tree case tpd.EmptyTree => tpd.ValDef(sym) } diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 9cfd85c9e433..b92fa85cceb4 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4058,11 +4058,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Tree of this definition * - * If this symbol `isClassDef` it will return `a `ClassDef`, - * if this symbol `isTypeDef` it will return `a `TypeDef`, - * if this symbol `isValDef` it will return `a `ValDef`, - * if this symbol `isDefDef` it will return `a `DefDef` - * if this symbol `isBind` it will return `a `Bind`, + * If this symbol `isClassDef` it will return a `ClassDef`, + * if this symbol `isTypeDef` it will return a `TypeDef`, + * if this symbol `isDefDef` it will return a `DefDef`, + * if this symbol `isBind` it will return a `Bind`, + * if this symbol `isValDef` it will return a `ValDef`, + * or a `DefDef` (as the compiler can replace val class members with defs during compilation), * else will throw * * **Warning**: avoid using this method in macros. diff --git a/tests/pos-macros/i22584/Macro.scala b/tests/pos-macros/i22584/Macro.scala new file mode 100644 index 000000000000..f0b9c7409520 --- /dev/null +++ b/tests/pos-macros/i22584/Macro.scala @@ -0,0 +1,30 @@ +//> using options -Yretain-trees + +import scala.quoted.* + +object Macros { + + inline def myMacro[A]: Unit = ${ myMacroImpl[A] } + + private def myMacroImpl[A](using quotes: Quotes, tpe: Type[A]): Expr[Unit] = { + import quotes.reflect.* + + val typeRepr = TypeRepr.of[A] + val typeSymbol = typeRepr.typeSymbol + + val caseFieldSymbols: List[Symbol] = typeSymbol.fieldMembers + val caseFieldValOrDefDefs: List[ValDef | DefDef] = + caseFieldSymbols.sortBy(_.toString).map { + _.tree match { + case valDef: ValDef => valDef + case defDef: DefDef => defDef + case _ => report.errorAndAbort("???") + } + } + + val expected = "List(DefDef(boolean,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Boolean)],Select(This(Ident(MyClass1)),boolean)), DefDef(finalVal,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)],Select(This(Ident(MyClass1)),finalVal)), DefDef(int,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Int)],Select(This(Ident(MyClass1)),int)), DefDef(string,List(List()),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)],Select(This(Ident(MyClass1)),string)))" + assert(caseFieldValOrDefDefs.toString == expected) + + '{ () } + } +} diff --git a/tests/pos-macros/i22584/Main.scala b/tests/pos-macros/i22584/Main.scala new file mode 100644 index 000000000000..4b6442078f96 --- /dev/null +++ b/tests/pos-macros/i22584/Main.scala @@ -0,0 +1,4 @@ +import Types.* + +@main def main() = + Macros.myMacro[MyClass1] diff --git a/tests/pos-macros/i22584/Types.scala b/tests/pos-macros/i22584/Types.scala new file mode 100644 index 000000000000..01560035ca60 --- /dev/null +++ b/tests/pos-macros/i22584/Types.scala @@ -0,0 +1,9 @@ +object Types { + final case class MyClass1( + int: Int, + string: String, + boolean: Boolean, + ) { + final val finalVal: String = "result" + } +}