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/05/16 18:58:58 UTC

[groovy] 01/02: GROOVY-5523, GROOVY-7753, GROOVY-9972: STC: refactor ternary checks

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

commit 5591f88592b588b63fd3f9a362cb4b3100b0deb5
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun May 16 13:47:04 2021 -0500

    GROOVY-5523, GROOVY-7753, GROOVY-9972: STC: refactor ternary checks
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  86 ++++++++------
 src/test/groovy/bugs/Groovy7753.groovy             | 101 +++++++++++++++++
 src/test/groovy/bugs/Groovy7753Bug.groovy          |  94 ---------------
 .../transform/stc/TernaryOperatorSTCTest.groovy    | 126 +++++++++++----------
 4 files changed, 220 insertions(+), 187 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 101ad5e..e210cbd 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -37,6 +37,7 @@ import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
 import org.codehaus.groovy.ast.ConstructorNode;
 import org.codehaus.groovy.ast.DynamicVariable;
 import org.codehaus.groovy.ast.FieldNode;
@@ -101,7 +102,6 @@ import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.ast.tools.WideningCategories;
 import org.codehaus.groovy.classgen.ReturnAdder;
 import org.codehaus.groovy.classgen.asm.InvocationWriter;
-import org.codehaus.groovy.classgen.asm.util.TypeUtil;
 import org.codehaus.groovy.control.CompilationUnit;
 import org.codehaus.groovy.control.ErrorCollector;
 import org.codehaus.groovy.control.ResolveVisitor;
@@ -212,6 +212,7 @@ import static org.codehaus.groovy.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VE
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isBigDecimalType;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isBigIntegerType;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isClassType;
+import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isDynamicTyped;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isGStringType;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isObjectType;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveBoolean;
@@ -231,7 +232,6 @@ import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isWrapperFloat;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isWrapperInteger;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isWrapperLong;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isWrapperShort;
-import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isWrapperVoid;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
 import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -1397,7 +1397,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param arguments the constructor arguments
      */
     protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) {
-        if (isObjectType(node) || TypeUtil.isDynamicTyped(node)) {
+        if (isObjectType(node) || isDynamicTyped(node)) {
             // in that case, we are facing a list constructor assigned to a def or object
             return null;
         }
@@ -1896,14 +1896,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             } else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
                 storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
             }
+
+            typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
+
             value.visit(this);
             ClassNode rType = getType(value);
             if (value instanceof ConstructorCallExpression) {
                 inferDiamondType((ConstructorCallExpression) value, lType);
             }
 
-            BinaryExpression bexp = assignX(target, value, position);
-            typeCheckAssignment(bexp, target, lType, value, getResultType(lType, ASSIGN, rType, bexp));
+            BinaryExpression dummy = typeCheckingContext.popEnclosingBinaryExpression();
+            typeCheckAssignment(dummy, target, lType, value, getResultType(lType, ASSIGN, rType, dummy));
         }
     }
 
@@ -1937,7 +1940,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (!checkCompatibleAssignmentTypes(forLoopVariableType, componentType)) {
                 addStaticTypeError("Cannot loop with element of type " + prettyPrintType(forLoopVariableType) + " with collection of type " + prettyPrintType(collectionType), forLoop);
             }
-            if (!TypeUtil.isDynamicTyped(forLoopVariableType)) {
+            if (!isDynamicTyped(forLoopVariableType)) {
                 // user has specified a type, prefer it over the inferred type
                 componentType = forLoopVariableType;
             }
@@ -2212,9 +2215,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
         MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
         if (enclosingMethod != null && !enclosingMethod.isVoidMethod()) {
-            if (!isNullConstant(expression)
-                    && !isPrimitiveVoid(type)
-                    && !isWrapperVoid(type)
+            if (!isNullConstant(expression) && !isPrimitiveVoid(getUnwrapper(type))
                     && !checkCompatibleAssignmentTypes(enclosingMethod.getReturnType(), type, null, false)) {
                 if (!extension.handleIncompatibleReturnType(statement, type)) {
                     addStaticTypeError("Cannot return value of type " + prettyPrintType(type) + " on method returning type " + prettyPrintType(enclosingMethod.getReturnType()), expression);
@@ -4214,7 +4215,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         ClassNode resultType;
         if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
-            resultType = checkForTargetType(expression, UNKNOWN_PARAMETER_TYPE);
+            resultType = checkForTargetType(trueExpression, UNKNOWN_PARAMETER_TYPE);
         } else if (isNullConstant(trueExpression) || (isEmptyCollection(trueExpression)
                 && isOrImplements(typeOfTrue, typeOfFalse))) { // [] : List/Collection/Iterable
             resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
@@ -4239,32 +4240,29 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         ClassNode targetType = null;
         MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
-        BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
-        if (enclosingBinaryExpression instanceof DeclarationExpression
-                && isAssignment(enclosingBinaryExpression.getOperation().getType())
-                && isTypeSource(expr, enclosingBinaryExpression.getRightExpression())) {
-            targetType = enclosingBinaryExpression.getLeftExpression().getType();
-        } else if (currentField != null
-                && isTypeSource(expr, currentField.getInitialExpression())) {
-            targetType = currentField.getType();
-        } else if (currentProperty != null
-                && isTypeSource(expr, currentProperty.getInitialExpression())) {
-            targetType = currentProperty.getType();
-        } else if (enclosingMethod != null) {
-            // TODO: try enclosingMethod's code with isTypeSource(expr, ...)
-            targetType = enclosingMethod.getReturnType();
-        } // TODO: closure parameter default expression
+        BinaryExpression enclosingExpression = typeCheckingContext.getEnclosingBinaryExpression();
+        if (enclosingExpression != null
+                && isAssignment(enclosingExpression.getOperation().getType())
+                && isTypeSource(expr, enclosingExpression.getRightExpression())) {
+            targetType = getDeclaredOrInferredType(enclosingExpression.getLeftExpression());
+        } else if (enclosingMethod != null
+                && !enclosingMethod.isVoidMethod()
+                && isTypeSource(expr, enclosingMethod)) {
+             targetType = enclosingMethod.getReturnType();
+        }
+
+        if (targetType == null) return sourceType;
 
         if (expr instanceof ConstructorCallExpression) {
-            if (targetType == null) targetType = sourceType;
             inferDiamondType((ConstructorCallExpression) expr, targetType);
-        } else if (targetType != null && !isPrimitiveType(getUnwrapper(targetType))
+        } else if (!isPrimitiveType(getUnwrapper(targetType))
                 && !isObjectType(targetType) && missesGenericsTypes(sourceType)) {
             // unchecked assignment with ternary/elvis, like "List<T> list = listOfT ?: []"
             // the inferred type is the RHS type "completed" with generics information from LHS
             return GenericsUtils.parameterizeType(targetType, sourceType.getPlainNodeReference());
         }
-        return targetType != null && sourceType == UNKNOWN_PARAMETER_TYPE ? targetType : sourceType;
+
+        return sourceType != UNKNOWN_PARAMETER_TYPE ? sourceType : targetType;
     }
 
     private static boolean isTypeSource(final Expression expr, final Expression right) {
@@ -4275,6 +4273,32 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return expr == right;
     }
 
+    private static boolean isTypeSource(final Expression expr, final MethodNode mNode) {
+        boolean[] returned = new boolean[1];
+
+        mNode.getCode().visit(new CodeVisitorSupport() {
+            @Override
+            public void visitReturnStatement(final ReturnStatement returnStatement) {
+                if (isTypeSource(expr, returnStatement.getExpression())) {
+                    returned[0] = true;
+                }
+            }
+            @Override
+            public void visitClosureExpression(final ClosureExpression expression) {
+            }
+        });
+
+        if (!returned[0]) {
+            new ReturnAdder(returnStatement -> {
+                if (isTypeSource(expr, returnStatement.getExpression())) {
+                    returned[0] = true;
+                }
+            }).visitMethod(mNode);
+        }
+
+        return returned[0];
+    }
+
     private static boolean isEmptyCollection(final Expression expr) {
         return isEmptyList(expr) || isEmptyMap(expr);
     }
@@ -5630,12 +5654,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private static Map<GenericsTypeName, GenericsType> extractPlaceHolders(final MethodNode method, ClassNode receiver, final ClassNode declaringClass) {
-        if (isObjectType(declaringClass)) {
-            Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
-            if (method != null) addMethodLevelDeclaredGenerics(method, resolvedPlaceholders);
-            return resolvedPlaceholders;
-        }
-
         Map<GenericsTypeName, GenericsType> resolvedPlaceholders = null;
         if (isPrimitiveType(receiver) && !isPrimitiveType(declaringClass)) {
             receiver = getWrapper(receiver);
diff --git a/src/test/groovy/bugs/Groovy7753.groovy b/src/test/groovy/bugs/Groovy7753.groovy
new file mode 100644
index 0000000..e3213db
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7753.groovy
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy7753 {
+
+    @Test
+    void testGroovy7753() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                x ? [x] : Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void testGroovy7753WithReturn() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                return x ? [x] : Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void testGroovy7753WithReturn2() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                return x ? Collections.emptyList() : [x]
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void test2() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                Collections.emptyList()
+            }
+
+            getStrings()
+        '''
+    }
+
+    @Test
+    void test3() {
+        assertScript '''
+            @groovy.transform.Field
+            String x = "X"
+
+            @groovy.transform.CompileStatic
+            public List<String> getStrings() {
+                [x]
+            }
+
+            getStrings()
+        '''
+    }
+}
diff --git a/src/test/groovy/bugs/Groovy7753Bug.groovy b/src/test/groovy/bugs/Groovy7753Bug.groovy
deleted file mode 100644
index b11dcc2..0000000
--- a/src/test/groovy/bugs/Groovy7753Bug.groovy
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package groovy.bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy7753Bug extends GroovyTestCase {
-
-    void testGroovy7753() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            x ? [x] : Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void testGroovy7753WithReturn() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            return x ? [x] : Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void testGroovy7753WithReturn2() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            return x ? Collections.emptyList() : [x]
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void test2() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            Collections.emptyList()
-        }
-        
-        getStrings()
-        '''
-    }
-
-    void test3() {
-        assertScript '''
-        @groovy.transform.Field
-        String x = "X"
-        
-        @groovy.transform.CompileStatic
-        public List<String> getStrings() {
-            [x]
-        }
-        
-        getStrings()
-        '''
-    }
-}
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index d7c7da6..a55f158 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -19,111 +19,112 @@
 package groovy.transform.stc
 
 /**
- * Unit tests for static type checking : ternary operator tests.
+ * Unit tests for static type checking : ternary operator.
  */
 class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
 
-    void testByteByteTernary() {
+    void testByteByte() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == byte_TYPE
             })
             def y = true?(byte)1:(byte)0
         '''
     }
-    void testShortShortTernary() {
+
+    void testShortShort() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == short_TYPE
             })
             def y = true?(short)1:(short)0
         '''
     }
 
-    void testIntIntTernary() {
+    void testIntInt() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == int_TYPE
             })
             def y = true?1:0
         '''
     }
 
-    void testLongLongTernary() {
+    void testLongLong() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == long_TYPE
             })
             def y = true?1L:0L
         '''
     }
 
-    void testFloatFloatTernary() {
+    void testFloatFloat() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == float_TYPE
             })
             def y = true?1f:0f
         '''
     }
 
-    void testDoubleDoubleTernary() {
+    void testDoubleDouble() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == double_TYPE
             })
             def y = true?1d:0d
         '''
     }
 
-    void testBoolBoolTernary() {
+    void testBoolBool() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == boolean_TYPE
             })
             def y = true?true:false
         '''
     }
 
-    void testDoubleFloatTernary() {
+    void testDoubleFloat() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == double_TYPE
             })
             def y = true?1d:1f
         '''
     }
 
-    void testDoubleDoubleTernaryWithBoxedTypes() {
+    void testDoubleDoubleWithBoxedTypes() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):new Double(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithBoxedTypes() {
+    void testDoubleFloatWithBoxedTypes() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):new Float(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithOneBoxedType() {
+    void testDoubleFloatWithOneBoxedType1() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?1d:new Float(1f)
         '''
     }
 
-    void testDoubleFloatTernaryWithOneBoxedType2() {
+    void testDoubleFloatWithOneBoxedType2() {
         assertScript '''
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Double_TYPE
             })
             def y = true?new Double(1d):1f
@@ -131,49 +132,56 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-5523
-    void testTernaryOperatorWithNull() {
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? new File(str) : null
-        }
+    void testNull1() {
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? new File(str) : null
+            }
         '''
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? null : new File(str)
-        }
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? null : new File(str)
+            }
         '''
-        assertScript '''File findFile() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
-            })
-            File f = str ? null : null
-        }
+        assertScript '''
+            def findFile() {
+                String str = ""
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == make(File)
+                })
+                File f = str ? null : null
+            }
         '''
     }
-    void testElvisOperatorWithNull() {
-        assertScript '''String findString() {
-            String str = ""
-            @ASTTest(phase=INSTRUCTION_SELECTION, value= {
-                assert node.getNodeMetaData(INFERRED_TYPE) == STRING_TYPE
-            })
-            String f = str ?: null
-        }
+
+    void testNull2() {
+        assertScript '''
+            def test(String str) {
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    assert node.getNodeMetaData(INFERRED_TYPE) == STRING_TYPE
+                })
+                String s = str ?: null
+            }
+
+            assert test('x') == 'x'
+            assert test('') == null
         '''
     }
 
     // GROOVY-5734
-    void testNullInTernary() {
+    void testNull3() {
         assertScript '''
-            Integer someMethod() { (false) ? null : 8 }
-            assert someMethod() == 8
+            Integer test() { false ? null : 42 }
+
+            assert test() == 42
         '''
     }
 }
-