Skip to content

Commit e71e350

Browse files
committed
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:
1 parent 9dc9895 commit e71e350

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

src/main/scala/scala/async/internal/AsyncBase.scala

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,26 @@ abstract class AsyncBase {
4343
(body: c.Expr[T])
4444
(execContext: c.Expr[futureSystem.ExecContext]): c.Expr[futureSystem.Fut[T]] = {
4545
import c.universe._
46-
4746
val asyncMacro = AsyncMacro(c, self)
4847

48+
val isPresentationCompiler = c.universe.getClass.toString.contains("ScalaPresentationCompiler")
49+
4950
val code = asyncMacro.asyncTransform[T](
5051
body.tree.asInstanceOf[asyncMacro.global.Tree],
5152
execContext.tree.asInstanceOf[asyncMacro.global.Tree]
52-
)(implicitly[c.WeakTypeTag[T]].asInstanceOf[asyncMacro.global.WeakTypeTag[T]]).asInstanceOf[Tree]
53-
54-
// Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges
55-
for (t <- code)
56-
t.pos = t.pos.makeTransparent
53+
)(implicitly[c.WeakTypeTag[T]].asInstanceOf[asyncMacro.global.WeakTypeTag[T]]).asInstanceOf[Tree]
5754

5855
AsyncUtils.vprintln(s"async state machine transform expands to:\n ${code}")
59-
c.Expr[futureSystem.Fut[T]](code)
56+
val result = if (isPresentationCompiler) {
57+
asyncMacro.suppressExpansion()
58+
c.macroApplication
59+
} else {
60+
// Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges
61+
for (t <- code)
62+
t.pos = t.pos.makeTransparent
63+
code
64+
}
65+
c.Expr[futureSystem.Fut[T]](result)
6066
}
6167

6268
protected[async] def awaitMethod(u: Universe)(asyncMacroSymbol: u.Symbol): u.Symbol = {

src/main/scala/scala/async/internal/AsyncMacro.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,21 @@ private[async] trait AsyncMacro
3232
lazy val macroPos = macroApplication.pos.makeTransparent
3333
def atMacroPos(t: global.Tree) = global.atPos(macroPos)(t)
3434

35+
def suppressExpansion() {
36+
// Have your cake : Scala IDE sees original trees and hyperlinking, etc within async blocks "Just Works"
37+
// Eat it too : (domain specific errors like "unsupported use of await"
38+
//
39+
// TODO roll this idea back into scala/scala
40+
def suppress(globalOrAnalzer: Any) = {
41+
type Suppress = { def suppressMacroExpansion(a: Object): Object }
42+
globalOrAnalzer.asInstanceOf[Suppress].suppressMacroExpansion(macroApplication)
43+
}
44+
try {
45+
suppress(global) // 2.10.x
46+
} catch {
47+
case _: NoSuchMethodException =>
48+
suppress(global.analyzer) // 2.11
49+
}
50+
}
51+
3552
}

0 commit comments

Comments
 (0)