Disclaimer: this code is not of practical use and serves education purposes only.
tl;dr: most of it is just a result of me trying to debug the issue, so only first 3 snippets matter.
Here is the macro definition:
def tx[T](ds: GraphDatabaseService)(block: => T): Option[T] =
macro txmacros.blockTxImpl[T]
Here is the implementation:
def blockTxImpl[T: c.WeakTypeTag](c: whitebox.Context)(ds: c.Tree)(block: c.Tree):
c.Tree = {
import c.universe._
q"""
val tx = $ds.beginTx()
val newRetVal = try {
val retVal = {
$block
}
tx.success()
Option(retVal)
} catch {
case _ : Throwable =>
tx.failure()
None
} finally {
tx.close()
}
newRetVal
"""
}
And here is how it's called:
val nodePropK5 = tx(db) {
// simplified for brevity
val node = db.find(label, "key", 100).iterator().next()
node.getProperty("k5", 300)
}
nodePropK5 should be (Some(200))
The whole project can be found to https://github.com/cdshines/txMacro/ (ready to be built an run).
Such an invocation fails with the following message:
[error] symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp
[trace] Stack trace suppressed: run last core/test:compile for the full output.
[error] (core/test:compile) scala.reflect.internal.FatalError: symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp
If I, however, change the problematic code to
val nodePropK5 = tx(db) {
db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
}
the return value is Some(300)
as expected. Adding lines that don't declare new variables (or use node
) doesn't break the behavior, while
val nodePropK5 = tx(db) {
db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
val x = 5
x
}
results in the same message.
Another peculiar thing: if I print the failing block
during macro expansion, i get the following code:
{
val tx = MacroTest.this.db.beginTx();
val newRetVal = try {
val retVal = {
val node: org.neo4j.graphdb.Node = MacroTest.this.db.findNodesByLabelAndProperty(MacroTest.this.label, "k4", 100).iterator().next();
node.getProperty("k5", 300)
};
tx.success();
Option(retVal)
} catch {
case (_: Throwable) => {
tx.failure();
None
}
} finally tx.close();
newRetVal
}
Which, being manually substituted, works just fine.
What am I missing here? Am I free to assume that this is a compiler bug?