|
1 | 1 | package mill.define |
2 | 2 |
|
3 | | -// import sourcecode.Compat.Context |
4 | | -// import language.experimental.macros |
| 3 | +import scala.quoted.* |
5 | 4 |
|
6 | 5 | case class EnclosingClass(value: Class[_]) |
7 | 6 | object EnclosingClass { |
8 | 7 | def apply()(implicit c: EnclosingClass) = c.value |
9 | | - implicit def generate: EnclosingClass = ??? // macro impl |
10 | | - // def impl(c: Context): c.Tree = { |
11 | | - // import c.universe._ |
12 | | - // // q"new _root_.mill.define.EnclosingClass(classOf[$cls])" |
13 | | - // q"new _root_.mill.define.EnclosingClass(this.getClass)" |
14 | | - // } |
| 8 | + inline given generate: EnclosingClass = ${ impl } |
| 9 | + |
| 10 | + // TODO: copied from Task.scala |
| 11 | + private def withMacroOwner[T](using Quotes)(op: quotes.reflect.Symbol => T): T = { |
| 12 | + import quotes.reflect.* |
| 13 | + |
| 14 | + // In Scala 3, the top level splice of a macro is owned by a symbol called "macro" with the macro flag set, |
| 15 | + // but not the method flag. |
| 16 | + def isMacroOwner(sym: Symbol)(using Quotes): Boolean = |
| 17 | + sym.name == "macro" && sym.flags.is(Flags.Macro | Flags.Synthetic) && !sym.flags.is( |
| 18 | + Flags.Method |
| 19 | + ) |
| 20 | + |
| 21 | + def loop(owner: Symbol): T = |
| 22 | + if owner.isPackageDef || owner == Symbol.noSymbol then |
| 23 | + report.errorAndAbort( |
| 24 | + "Cannot find the owner of the macro expansion", |
| 25 | + Position.ofMacroExpansion |
| 26 | + ) |
| 27 | + else if isMacroOwner(owner) then op(owner.owner) // Skip the "macro" owner |
| 28 | + else loop(owner.owner) |
| 29 | + |
| 30 | + loop(Symbol.spliceOwner) |
| 31 | + } |
| 32 | + |
| 33 | + def impl(using Quotes): Expr[EnclosingClass] = withMacroOwner { owner => |
| 34 | + import quotes.reflect.* |
| 35 | + |
| 36 | + def enclosingClass(sym: Symbol): Symbol = |
| 37 | + if sym.isPackageDef || sym == Symbol.noSymbol then |
| 38 | + report.errorAndAbort( |
| 39 | + "Cannot find the enclosing class of the macro expansion", |
| 40 | + Position.ofMacroExpansion |
| 41 | + ) |
| 42 | + else if sym.isClassDef then sym |
| 43 | + else enclosingClass(sym.owner) |
| 44 | + |
| 45 | + if owner.flags.is(Flags.Method) then |
| 46 | + val cls = enclosingClass(owner) |
| 47 | + val ref = This(cls).asExprOf[Any] |
| 48 | + '{ new EnclosingClass($ref.getClass) } |
| 49 | + else |
| 50 | + report.errorAndAbort( |
| 51 | + "EnclosingClass.generate can only be used within a method", |
| 52 | + Position.ofMacroExpansion |
| 53 | + ) |
| 54 | + } |
15 | 55 | } |
0 commit comments