aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-11-20 13:22:16 +0100
committerJason Zaugg <jzaugg@gmail.com>2013-11-20 14:33:15 +0100
commite71e3506d31bcc08cd2f0a030c929bc0d1b62bc2 (patch)
treeaeca7642e723d5efe11ca9adef618f9ec29129f4
parent9dc9895dd1ebef82db83d0ac2723eb710eed73de (diff)
downloadscala-async-e71e3506d31bcc08cd2f0a030c929bc0d1b62bc2.tar.gz
scala-async-e71e3506d31bcc08cd2f0a030c929bc0d1b62bc2.tar.bz2
scala-async-e71e3506d31bcc08cd2f0a030c929bc0d1b62bc2.zip
Return original macro application under presentation compiler.
Right now, the body of an async block suffers from diminished IDE support: most notably hyperlinking doesn't work [1]. During the course of the blackbox/whitebox macro discussion, we've often talked about how the former give us the latitude to simply disable macro expansion in the IDE so we could get these features working again, at the cost of losing domain specific errors, such as "await must not be used under a nested function". But why not have our cake and eat too? This commit detects if we are running the presentation compiler and, after running our regular macro, returns the original macro application. We need to annotate that tree to prevent the typechecker from stubbornly calling our macro again. EXPERIMENTAL NOTE: This logic shouldn't live in macros: this is just a short term measure. If these experiments in async prove successful, we'll roll something similar into the macro expansion engine itself. TODO: as a performance optimization, we could just run the "unsupported await" checks, and avoid doing the more expensive state machine transformation. [1] https://www.assembla.com/spaces/scala-ide/tickets/1001449-code-navigation-fails-when-macros-are-used#/activity/ticket:
-rw-r--r--src/main/scala/scala/async/internal/AsyncBase.scala20
-rw-r--r--src/main/scala/scala/async/internal/AsyncMacro.scala17
2 files changed, 30 insertions, 7 deletions
diff --git a/src/main/scala/scala/async/internal/AsyncBase.scala b/src/main/scala/scala/async/internal/AsyncBase.scala
index 8cacb40..9f07182 100644
--- a/src/main/scala/scala/async/internal/AsyncBase.scala
+++ b/src/main/scala/scala/async/internal/AsyncBase.scala
@@ -43,20 +43,26 @@ abstract class AsyncBase {
(body: c.Expr[T])
(execContext: c.Expr[futureSystem.ExecContext]): c.Expr[futureSystem.Fut[T]] = {
import c.universe._
-
val asyncMacro = AsyncMacro(c, self)
+ val isPresentationCompiler = c.universe.getClass.toString.contains("ScalaPresentationCompiler")
+
val code = asyncMacro.asyncTransform[T](
body.tree.asInstanceOf[asyncMacro.global.Tree],
execContext.tree.asInstanceOf[asyncMacro.global.Tree]
- )(implicitly[c.WeakTypeTag[T]].asInstanceOf[asyncMacro.global.WeakTypeTag[T]]).asInstanceOf[Tree]
-
- // Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges
- for (t <- code)
- t.pos = t.pos.makeTransparent
+ )(implicitly[c.WeakTypeTag[T]].asInstanceOf[asyncMacro.global.WeakTypeTag[T]]).asInstanceOf[Tree]
AsyncUtils.vprintln(s"async state machine transform expands to:\n ${code}")
- c.Expr[futureSystem.Fut[T]](code)
+ val result = if (isPresentationCompiler) {
+ asyncMacro.suppressExpansion()
+ c.macroApplication
+ } else {
+ // Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges
+ for (t <- code)
+ t.pos = t.pos.makeTransparent
+ code
+ }
+ c.Expr[futureSystem.Fut[T]](result)
}
protected[async] def awaitMethod(u: Universe)(asyncMacroSymbol: u.Symbol): u.Symbol = {
diff --git a/src/main/scala/scala/async/internal/AsyncMacro.scala b/src/main/scala/scala/async/internal/AsyncMacro.scala
index ee49923..d030367 100644
--- a/src/main/scala/scala/async/internal/AsyncMacro.scala
+++ b/src/main/scala/scala/async/internal/AsyncMacro.scala
@@ -32,4 +32,21 @@ private[async] trait AsyncMacro
lazy val macroPos = macroApplication.pos.makeTransparent
def atMacroPos(t: global.Tree) = global.atPos(macroPos)(t)
+ def suppressExpansion() {
+ // Have your cake : Scala IDE sees original trees and hyperlinking, etc within async blocks "Just Works"
+ // Eat it too : (domain specific errors like "unsupported use of await"
+ //
+ // TODO roll this idea back into scala/scala
+ def suppress(globalOrAnalzer: Any) = {
+ type Suppress = { def suppressMacroExpansion(a: Object): Object }
+ globalOrAnalzer.asInstanceOf[Suppress].suppressMacroExpansion(macroApplication)
+ }
+ try {
+ suppress(global) // 2.10.x
+ } catch {
+ case _: NoSuchMethodException =>
+ suppress(global.analyzer) // 2.11
+ }
+ }
+
}