summaryrefslogtreecommitdiff
path: root/core/src/main/scala
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-11-04 22:49:16 -0700
committerLi Haoyi <haoyi.sg@gmail.com>2017-11-04 22:49:16 -0700
commit6e1243ba2eb2acf3f1fb2b397c0c22ea0e746f1a (patch)
tree95f5165a2a73d6c2159ea5f2e6cc012596bc3b84 /core/src/main/scala
parent57087e0f2b91d7b906ddda6a7078fa36fdabbbcb (diff)
downloadmill-6e1243ba2eb2acf3f1fb2b397c0c22ea0e746f1a.tar.gz
mill-6e1243ba2eb2acf3f1fb2b397c0c22ea0e746f1a.tar.bz2
mill-6e1243ba2eb2acf3f1fb2b397c0c22ea0e746f1a.zip
First pass at providing good compile errors to invalid uses of the `T{...}` macro: we should only allow you to `Target#apply()` on expressions whose values come from outside the `T{...}` block
Diffstat (limited to 'core/src/main/scala')
-rw-r--r--core/src/main/scala/forge/Target.scala26
1 files changed, 23 insertions, 3 deletions
diff --git a/core/src/main/scala/forge/Target.scala b/core/src/main/scala/forge/Target.scala
index 635acb8e..a829d433 100644
--- a/core/src/main/scala/forge/Target.scala
+++ b/core/src/main/scala/forge/Target.scala
@@ -51,13 +51,33 @@ object Target{
}
def impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Target[T]] = {
import c.universe._
+ def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_))
val bound = collection.mutable.Buffer.empty[(c.Tree, Symbol)]
- val OptionGet = c.universe.typeOf[Target[_]].member(TermName("apply"))
+ val targetApplySym = c.universe.typeOf[Target[_]].member(TermName("apply"))
// Derived from @olafurpg's
// https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608
+ val (startPos, endPos) = rec(t.tree)
+ .map(t => (t.pos.start, t.pos.end))
+ .reduce[(Int, Int)]{ case ((s1, e1), (s2, e2)) => (math.min(s1, s2), math.max(e1, e2))}
+
+ val macroSource = t.tree.pos.source
val transformed = c.internal.typingTransform(t.tree) {
- case (t @ q"$fun.apply()", api) if t.symbol == OptionGet =>
+ case (t @ q"$fun.apply()", api) if t.symbol == targetApplySym =>
+
+ val used = rec(t)
+ val banned = used.filter(x =>
+ x.symbol.pos.source == macroSource &&
+ x.symbol.pos.start >= startPos &&
+ x.symbol.pos.end <= endPos
+ )
+ if (banned.hasNext){
+ val banned0 = banned.next()
+ c.abort(
+ banned0.pos,
+ "Target#apply() call cannot use `" + banned0.symbol + "` defined within the T{...} block"
+ )
+ }
val tempName = c.freshName(TermName("tmp"))
val tempSym = c.internal.newTermSymbol(api.currentOwner, tempName)
c.internal.setInfo(tempSym, t.tpe)
@@ -77,7 +97,7 @@ object Target{
def wrapCached[T](c: Context)(t: c.Tree) = {
import c.universe._
val owner = c.internal.enclosingOwner
- val ownerIsCacherClass = owner.owner.asClass.baseClasses.exists(_.fullName == "forge.Target.Cacher")
+ val ownerIsCacherClass = owner.owner.isClass && owner.owner.asClass.baseClasses.exists(_.fullName == "forge.Target.Cacher")
if (ownerIsCacherClass && !owner.isMethod){
c.abort(