This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new a708f64  GROOVY-6510: `@Category`: preserve implicit-this semantic 
within closure
a708f64 is described below

commit a708f649e86f832390f40c9374c530234a7efa4b
Author: Eric Milles <[email protected]>
AuthorDate: Thu Feb 10 15:42:07 2022 -0600

    GROOVY-6510: `@Category`: preserve implicit-this semantic within closure
---
 .../transform/CategoryASTTransformation.java       | 21 +++++--
 src/test/groovy/lang/CategoryAnnotationTest.groovy | 67 +++++++++++++++++++---
 2 files changed, 77 insertions(+), 11 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java 
b/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
index 26b1dea..72662fa 100644
--- a/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java
@@ -32,6 +32,7 @@ import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.ClosureExpression;
 import org.codehaus.groovy.ast.expr.DeclarationExpression;
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.PropertyExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
@@ -47,6 +48,8 @@ import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Set;
 
+import static java.util.Collections.addAll;
+import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression;
 import static org.codehaus.groovy.ast.tools.ClosureUtils.getParametersSafe;
 import static org.codehaus.groovy.ast.tools.ClosureUtils.hasImplicitParameter;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
@@ -119,6 +122,8 @@ public class CategoryASTTransformation implements 
ASTTransformation {
         varStack.add(names);
 
         ClassCodeExpressionTransformer transformer = new 
ClassCodeExpressionTransformer() {
+            private boolean inClosure; // GROOVY-6510: track closure 
containment
+
             private void addVariablesToStack(final Parameter[] parameter) {
                 Set<String> names = new HashSet<>(varStack.getLast());
                 for (Parameter p : parameter) names.add(p.getName());
@@ -144,17 +149,28 @@ public class CategoryASTTransformation implements 
ASTTransformation {
                         Expression thisExpression = createThisExpression();
                         thisExpression.setSourcePosition(ve);
                         return thisExpression;
-                    } else if (!ve.isSuperExpression() && 
!varStack.getLast().contains(ve.getName())) {
+                    } else if (!inClosure && !ve.isSuperExpression() && 
!varStack.getLast().contains(ve.getName())) {
                         PropertyExpression pe = new 
PropertyExpression(createThisExpression(), ve.getName());
                         pe.setSourcePosition(ve);
                         return pe;
                     }
+                } else if (expression instanceof MethodCallExpression) {
+                    MethodCallExpression mce = (MethodCallExpression) 
expression;
+                    if (inClosure && mce.isImplicitThis() && 
isThisExpression(mce.getObjectExpression())) {
+                        // GROOVY-6510: preserve implicit-this semantics
+                        mce.setArguments(transform(mce.getArguments()));
+                        mce.setMethod(transform(mce.getMethod()));
+                        return mce;
+                    }
                 } else if (expression instanceof ClosureExpression) {
                     ClosureExpression ce = (ClosureExpression) expression;
                     addVariablesToStack(hasImplicitParameter(ce) ? 
params(param(ClassHelper.OBJECT_TYPE, "it")) : getParametersSafe(ce));
                     
ce.getVariableScope().putReferencedLocalVariable(selfParameter.get());
+                    addAll(varStack.getLast(), "owner", "delegate", 
"thisObject");
+                    boolean closure = inClosure; inClosure = true;
                     ce.getCode().visit(this);
                     varStack.removeLast();
+                    inClosure = closure;
                 }
                 return super.transform(expression);
             }
@@ -176,9 +192,6 @@ public class CategoryASTTransformation implements 
ASTTransformation {
 
             @Override
             public void visitClosureExpression(final ClosureExpression 
expression) {
-                addVariablesToStack(getParametersSafe(expression));
-                super.visitClosureExpression(expression);
-                varStack.removeLast();
             }
 
             @Override
diff --git a/src/test/groovy/lang/CategoryAnnotationTest.groovy 
b/src/test/groovy/lang/CategoryAnnotationTest.groovy
index 382b660..593caaa 100644
--- a/src/test/groovy/lang/CategoryAnnotationTest.groovy
+++ b/src/test/groovy/lang/CategoryAnnotationTest.groovy
@@ -100,7 +100,7 @@ final class CategoryAnnotationTest {
             interface Guy {
                 Type getType()
             }
-            class MyGuyver implements Guy {
+            class McGuyver implements Guy {
                 Type type
             }
             @Category(Guy)
@@ -114,7 +114,7 @@ final class CategoryAnnotationTest {
             }
 
             def atype = new Type(name: 'String')
-            def onetest = new MyGuyver(type:atype)
+            def onetest = new McGuyver(type:atype)
 
             use(Naming) {
                 assert onetest.getTypeName() == onetest.getType().getName()
@@ -129,24 +129,77 @@ final class CategoryAnnotationTest {
                 String getName()
                 List getMessages()
             }
-            class MyGuyver implements Guy {
+            class McGuyver implements Guy {
                 List messages
                 String name
             }
             @Category(Guy)
             class Filtering {
                 List process() {
-                    this.messages.findAll{it.name != this.getName()}
+                    this.messages.findAll{ it.name != this.getName() }
                 }
             }
 
-            def onetest = new MyGuyver(name: 'coucou',
+            def onetest = new McGuyver(name: 'coucou',
                 messages: [['name':'coucou'], ['name':'test'], 
['name':'salut']]
             )
 
-            Guy.mixin Filtering
+            Guy.mixin(Filtering)
 
-            assert onetest.process() == onetest.messages.findAll{it.name != 
onetest.getName()}
+            assert onetest.process() == onetest.messages.findAll{ it.name != 
onetest.getName() }
+        '''
+    }
+
+    @Test // GROOVY-6510
+    void testClosureUsingImplicitThis() {
+        assertScript '''
+            @Category(Number)
+            class NumberCategory {
+                def foo() {
+                    def bar = { ->
+                        baz() // do not want "$this.baz()"
+                    }
+                    bar.resolveStrategy = Closure.DELEGATE_FIRST
+                    bar.delegate = new NumberDelegate(this)
+                    bar.call()
+                }
+            }
+
+            class NumberDelegate {
+                private final Number n
+                NumberDelegate(Number n) { this.n = n }
+                String baz() { 'number ' + n.intValue() }
+            }
+
+            use(NumberCategory) {
+                String result = 1.foo()
+                assert result == 'number 1'
+            }
+        '''
+
+        assertScript '''
+            @Category(Number)
+            class NumberCategory {
+                def foo() {
+                    def bar = { ->
+                        baz // do not want "$this.baz"
+                    }
+                    bar.resolveStrategy = Closure.DELEGATE_FIRST
+                    bar.delegate = new NumberDelegate(this)
+                    bar.call()
+                }
+            }
+
+            class NumberDelegate {
+                private final Number n
+                NumberDelegate(Number n) { this.n = n }
+                String getBaz() { 'number ' + n.intValue() }
+            }
+
+            use(NumberCategory) {
+                String result = 1.foo()
+                assert result == 'number 1'
+            }
         '''
     }
 

Reply via email to