The behavior of implicit closures is in my opinion broken when used inside of a category.
As you can see in the attached TestCase in a simplified example, I had the following scenario:
- first there is an utility method (in the example the method ClosureInvoker#setDelegateAndInvokeClosure(Closure)) that gets a closure, resets the delegate object of it and calls this closure.
- next we have an ExtendedClass which isn't really used in the example itself other than being used as "base"-class for the category/extension-methods. This is just because its own functionallity doesn't matter to get the buggy behavior. Just be ensured that it made more sense in the original context (e.g. it was passed to the utility method and used to setup the delegate object)
- now there is a category class with a method that invokes the utility method and passing an implicit closure to it (@see CategoryClass#doWorkWithImplicitClosure()). that implicit closure is invoking a method of the delegate object
- at last the testcase itself will call this category method and assert, that the delegate method was really called by the closure (@see ClosureInsideExtensionOrCategoryTest#testCategoryWithImplicitClosure())
As you can see in the additional test methods, the basic behavior does work as expected. But only in the special combination of an implicit closure inside of the category it fails.
As far as I can backtrack it, it seems that the AST-manipulations for category classes may do the damage. It appears to me, that it will manipulate ALL unqualified member-calls inside of any category-methods to be instead calls to the class given by the @Category-annotation. But this manipulation should in my opinion be escaped when entering the code-block of an implicit closure.
So refering to my example the method CategoryClass#doWorkWithImplicitClosure() seems to lead to the following code after AST-manipulation:
This will result in avoiding the whole resolve strategy of the closure, as there won't be any unqualified member calls inside of the closure at all.
Instead I would expect, that it would generate the following code:
which would be equivalent to ExtensionMethodClass#doWorkWithImplicitClosure(ExtendedClass)