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 2022/08/26 19:42:27 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-10217: STC: consider `instanceof` flow-type for binary expr LHS

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new eb9e36441e GROOVY-10217: STC: consider `instanceof` flow-type for binary expr LHS
eb9e36441e is described below

commit eb9e36441e8ea728faaeda1ef15b0fffd768a0f6
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Aug 26 14:30:43 2022 -0500

    GROOVY-10217: STC: consider `instanceof` flow-type for binary expr LHS
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 30 ++++++++++++----------
 .../transform/stc/TypeInferenceSTCTest.groovy      | 14 ++++++++++
 2 files changed, 31 insertions(+), 13 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 8b9549fc67..e3e0ef77cf 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -214,7 +214,6 @@ import static org.codehaus.groovy.syntax.Types.INTDIV;
 import static org.codehaus.groovy.syntax.Types.INTDIV_EQUAL;
 import static org.codehaus.groovy.syntax.Types.KEYWORD_IN;
 import static org.codehaus.groovy.syntax.Types.KEYWORD_INSTANCEOF;
-import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
 import static org.codehaus.groovy.syntax.Types.MINUS_MINUS;
 import static org.codehaus.groovy.syntax.Types.MOD;
 import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
@@ -641,7 +640,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 if (variable != null) {
                     ClassNode inferredType = getInferredTypeFromTempInfo(variable, (ClassNode) variable.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE));
-                    if (inferredType != null && !inferredType.getName().equals(ClassHelper.OBJECT)) {
+                    if (inferredType != null && !inferredType.getName().equals(ClassHelper.OBJECT) && !inferredType.equals(accessedVariable.getType())) {
                         vexp.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, inferredType);
                     }
                 }
@@ -778,29 +777,35 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         try {
             final Expression leftExpression = expression.getLeftExpression();
             final Expression rightExpression = expression.getRightExpression();
-            leftExpression.visit(this);
+            leftExpression.visit(this); final ClassNode lType, rType;
             SetterInfo setterInfo = removeSetterInfo(leftExpression);
             if (setterInfo != null) {
                 if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
                     return;
                 }
+                lType = getType(leftExpression);
             } else {
                 if (op == ASSIGN) {
-                    ClassNode lType = getOriginalDeclarationType(leftExpression);
+                    lType = getOriginalDeclarationType(leftExpression);
+
                     if (isFunctionalInterface(lType)) {
                         processFunctionalInterfaceAssignment(lType, rightExpression);
                     } else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
                         storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
                     }
+                } else {
+                    if (leftExpression instanceof VariableExpression && hasInferredReturnType(leftExpression)) {
+                        lType = getInferredReturnType(leftExpression); // GROOVY-10217
+                    } else {
+                        lType = getType(leftExpression);
+                    }
                 }
                 rightExpression.visit(this);
             }
-            ClassNode lType = getType(leftExpression);
-            ClassNode rType = getType(rightExpression);
-            if (isNullConstant(rightExpression)) {
-                if (!isPrimitiveType(lType))
-                    rType = UNKNOWN_PARAMETER_TYPE; // primitive types should be ignored as they will result in another failure
-            }
+
+            rType = isNullConstant(rightExpression) && !isPrimitiveType(lType)
+                    ? UNKNOWN_PARAMETER_TYPE
+                    : getType(rightExpression);
             BinaryExpression reversedBinaryExpression = binX(rightExpression, expression.getOperation(), leftExpression);
             ClassNode resultType = op == KEYWORD_IN
                     ? getResultType(rType, op, lType, reversedBinaryExpression)
@@ -809,7 +814,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 // in case of the "in" operator, the receiver and the arguments are reversed
                 // so we use the reversedExpression and get the target method from it
                 storeTargetMethod(expression, (MethodNode) reversedBinaryExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
-            } else if (op == LEFT_SQUARE_BRACKET
+            } else if (isArrayOp(op)
                     && leftExpression instanceof VariableExpression
                     && leftExpression.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE) == null) {
                 storeType(leftExpression, lType);
@@ -822,8 +827,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (leftExpression instanceof VariableExpression) {
                 VariableExpression leftVar = (VariableExpression) leftExpression;
                 if (leftVar.isClosureSharedVariable()) {
-                    // if left expression is a closure shared variable, we should check it twice
-                    // see GROOVY-5874
+                    // GROOVY-5874: if left expression is a closure shared variable, we should check it twice
                     typeCheckingContext.secondPassExpressions.add(new SecondPassExpression<Void>(expression));
                 }
             }
diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 76f66851bf..1cf3081463 100644
--- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -475,6 +475,20 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         ''', 'Cannot find matching method java.lang.Object#toUpperCase()'
     }
 
+    // GROOVY-10217
+    void testInstanceOfThenSubscriptOperator() {
+        assertScript '''
+            void test(Object o) {
+                if (o instanceof List) {
+                    assert o[0] == 1
+                    def x = (List) o
+                    assert x[0] == 1
+                }
+            }
+            test([1])
+        '''
+    }
+
     void testInstanceOfInferenceWithImplicitIt() {
         assertScript '''
         ['a', 'b', 'c'].each {