You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/08/18 16:50:06 UTC
[groovy] 01/03: GROOVY-10052: no re-visit for closure without
changed/shared variable(s)
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 6e4049e16f7a79a173fcbbc12b46f8bf86d1c75a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Apr 23 13:31:39 2021 -0500
GROOVY-10052: no re-visit for closure without changed/shared variable(s)
---
.../transform/stc/StaticTypeCheckingVisitor.java | 30 ++++++++++------
.../groovy/transform/stc/ClosuresSTCTest.groovy | 41 +++++++++++++++++++++-
2 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 876e34e..2f72981 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1923,8 +1923,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
typeCheckingContext.controlStructureVariables.remove(forLoop.getVariable());
}
}
- boolean typeChanged = isSecondPassNeededForControlStructure(varOrigType, oldTracker);
- if (typeChanged) visitForLoop(forLoop);
+ if (isSecondPassNeededForControlStructure(varOrigType, oldTracker)) {
+ visitForLoop(forLoop);
+ }
}
/**
@@ -2338,8 +2339,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
typeCheckingContext.isInStaticContext = false;
// collect every variable expression used in the closure body
- Map<VariableExpression, ClassNode> variableTypes = new HashMap<>();
- expression.getCode().visit(new VariableExpressionTypeMemoizer(variableTypes));
+ Map<VariableExpression, ClassNode> varTypes = new HashMap<>();
+ expression.getCode().visit(new VariableExpressionTypeMemoizer(varTypes, true));
Map<VariableExpression, List<ClassNode>> oldTracker = pushAssignmentTracking();
SharedVariableCollector collector = new SharedVariableCollector(getSourceUnit());
collector.visitClosureExpression(expression);
@@ -2393,9 +2394,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
typeCheckingContext.popEnclosingClosure();
-
- boolean typeChanged = isSecondPassNeededForControlStructure(variableTypes, oldTracker);
- if (typeChanged) visitClosureExpression(expression);
+ // check types of closure shared variables for change
+ if (isSecondPassNeededForControlStructure(varTypes, oldTracker)) {
+ visitClosureExpression(expression);
+ }
// restore original metadata
restoreVariableExpressionMetadata(variableMetadata);
@@ -5900,10 +5902,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected class VariableExpressionTypeMemoizer extends ClassCodeVisitorSupport {
+ private final boolean onlySharedVariables;
private final Map<VariableExpression, ClassNode> varOrigType;
public VariableExpressionTypeMemoizer(final Map<VariableExpression, ClassNode> varOrigType) {
+ this(varOrigType, false);
+ }
+
+ public VariableExpressionTypeMemoizer(final Map<VariableExpression, ClassNode> varOrigType, final boolean onlySharedVariables) {
this.varOrigType = varOrigType;
+ this.onlySharedVariables = onlySharedVariables;
}
@Override
@@ -5913,12 +5921,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
@Override
public void visitVariableExpression(final VariableExpression expression) {
- super.visitVariableExpression(expression);
Variable var = findTargetVariable(expression);
- if (var instanceof VariableExpression) {
+ if ((!onlySharedVariables || var.isClosureSharedVariable()) && var instanceof VariableExpression) {
VariableExpression ve = (VariableExpression) var;
- varOrigType.put(ve, ve.getNodeMetaData(INFERRED_TYPE));
+ ClassNode cn = ve.getNodeMetaData(INFERRED_TYPE);
+ if (cn == null) cn = ve.getOriginType();
+ varOrigType.put(ve, cn);
}
+ super.visitVariableExpression(expression);
}
}
}
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index b148665..0759138 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -18,6 +18,8 @@
*/
package groovy.transform.stc
+import groovy.test.NotYetImplemented
+
/**
* Unit tests for static type checking : closures.
*/
@@ -32,8 +34,8 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9079: no params to statically type check but shouldn't get NPE
void testClosureWithoutArgumentsExplicit() {
- // GROOVY-9079: no params to statically type check but shouldn't get NPE
assertScript '''
import java.util.concurrent.Callable
@@ -287,6 +289,43 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
''', 'Cannot find matching method A#m()'
}
+ // GROOVY-10052
+ void testClosureSharedVariable4() {
+ assertScript '''
+ String x
+ def f = { ->
+ x = Optional.of('x').orElseThrow{ new Exception() }
+ }
+ assert f() == 'x'
+ assert x == 'x'
+ '''
+ }
+
+ @NotYetImplemented // GROOVY-10052
+ void testClosureSharedVariable5() {
+ assertScript '''
+ def x
+ def f = { ->
+ x = Optional.of('x').orElseThrow{ new Exception() }
+ }
+ assert f() == 'x'
+ assert x == 'x'
+ '''
+ }
+
+ // GROOVY-10052
+ void testNotClosureSharedVariable() {
+ assertScript '''
+ String x = Optional.of('x').orElseThrow{ new Exception() }
+ def f = { ->
+ String y = Optional.of('y').orElseThrow{ new Exception() }
+ }
+
+ assert x == 'x'
+ assert f() == 'y'
+ '''
+ }
+
void testClosureCallAsAMethod() {
assertScript '''
Closure cl = { 'foo' }