@@ -37,14 +37,17 @@ object Splicer {
3737   * 
3838   *  See: `Staging` 
3939   */  
40-   def  splice (tree : Tree , pos : SourcePosition , classLoader : ClassLoader )(implicit  ctx : Context ):  Tree  =  tree match  {
40+   def  splice (tree : Tree , pos : SourcePosition , classLoader : ClassLoader )(given  ctx :  Context ):  Tree  =  tree match  {
4141    case  Quoted (quotedTree) =>  quotedTree
4242    case  _ => 
4343      val  interpreter  =  new  Interpreter (pos, classLoader)
44+       val  macroOwner  =  ctx.newSymbol(ctx.owner, NameKinds .UniqueName .fresh(nme.MACROkw ), Synthetic , defn.AnyType , coord =  tree.span)
4445      try  {
46+         given  Context  =  ctx.withOwner(macroOwner)
4547        //  Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
4648        val  interpretedExpr  =  interpreter.interpret[scala.quoted.QuoteContext  =>  scala.quoted.Expr [Any ]](tree)
47-         interpretedExpr.fold(tree)(macroClosure =>  PickledQuotes .quotedExprToTree(macroClosure(QuoteContext ())))
49+         val  interpretedTree  =  interpretedExpr.fold(tree)(macroClosure =>  PickledQuotes .quotedExprToTree(macroClosure(QuoteContext ())))
50+         checkEscapedVariables(interpretedTree, macroOwner).changeOwner(macroOwner, ctx.owner)
4851      }
4952      catch  {
5053        case  ex : CompilationUnit .SuspendException  => 
@@ -63,6 +66,50 @@ object Splicer {
6366      }
6467  }
6568
69+   /**  Checks that no symbol that whas generated within the macro expansion has an out of scope reference */  
70+   def  checkEscapedVariables (tree : Tree , expansionOwner : Symbol )(given  ctx :  Context ):  tree.type  = 
71+     new  TreeTraverser  {
72+       private [this ] var  locals  =  Set .empty[Symbol ]
73+       private  def  markDef (tree : Tree )(implicit  ctx : Context ):  Unit  =  tree match  {
74+         case  tree : DefTree  => 
75+           val  sym  =  tree.symbol
76+           if  (! locals.contains(sym))
77+             locals =  locals +  sym
78+         case  _ => 
79+       }
80+       def  traverse (tree : Tree )(given  ctx :  Context ):  Unit  = 
81+         def  traverseOver (lastEntered : Set [Symbol ]) = 
82+           try  traverseChildren(tree)
83+           finally  locals =  lastEntered
84+         tree match 
85+           case  tree : Ident  if  isEscapedVariable(tree.symbol) => 
86+             val  sym  =  tree.symbol
87+             ctx.error(em " While expanding a macro, a reference to  $sym was used outside the scope where it was defined " , tree.sourcePos)
88+           case  Block (stats, _) => 
89+             val  last  =  locals
90+             stats.foreach(markDef)
91+             traverseOver(last)
92+           case  CaseDef (pat, guard, body) => 
93+             val  last  =  locals
94+             //  mark all bindings
95+             new  TreeTraverser  {
96+               def  traverse (tree : Tree )(implicit  ctx : Context ):  Unit  =  {
97+                 markDef(tree)
98+                 traverseChildren(tree)
99+               }
100+             }.traverse(pat)
101+             traverseOver(last)
102+           case  _ => 
103+             markDef(tree)
104+             traverseChildren(tree)
105+       private  def  isEscapedVariable (sym : Symbol )(given  ctx :  Context ):  Boolean  = 
106+         sym.exists &&  ! sym.is(Package )
107+         &&  sym.owner.ownersIterator.contains(expansionOwner) //  symbol was generated within the macro expansion
108+         &&  ! locals.contains(sym) //  symbol is not in current scope
109+     }.traverse(tree)
110+     tree
111+ 
112+ 
66113  /**  Check that the Tree can be spliced. `${'{xyz}}` becomes `xyz` 
67114    *  and for `$xyz` the tree of `xyz` is interpreted for which the 
68115    *  resulting expression is returned as a `Tree` 
0 commit comments