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/03/14 18:07:26 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-5523, GROOVY-9972, GROOVY-9983, GROOVY-10357: STC: ternary checks
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
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new 609b3e1 GROOVY-5523, GROOVY-9972, GROOVY-9983, GROOVY-10357: STC: ternary checks
609b3e1 is described below
commit 609b3e1f4b405ea2494dcd357078a84c6a356962
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun May 16 13:47:04 2021 -0500
GROOVY-5523, GROOVY-9972, GROOVY-9983, GROOVY-10357: STC: ternary checks
---
.../transform/stc/StaticTypeCheckingVisitor.java | 130 +++++++++------
src/test/groovy/bugs/Groovy7753.groovy | 101 ++++++++++++
src/test/groovy/bugs/Groovy7753Bug.groovy | 94 -----------
.../groovy/transform/stc/GenericsSTCTest.groovy | 89 +++++++++++
.../transform/stc/TernaryOperatorSTCTest.groovy | 174 ++++++++++++++-------
5 files changed, 383 insertions(+), 205 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 4e3a82c..72e97ea 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;
@@ -4198,79 +4199,104 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
@Override
public void visitTernaryExpression(final TernaryExpression expression) {
Map<VariableExpression, List<ClassNode>> oldTracker = pushAssignmentTracking();
- // create a new temporary element in the if-then-else type info
- typeCheckingContext.pushTemporaryTypeInfo();
- expression.getBooleanExpression().visit(this);
+ typeCheckingContext.pushTemporaryTypeInfo(); // capture instanceof restriction
+ if (!(expression instanceof ElvisOperatorExpression)) {
+ expression.getBooleanExpression().visit(this);
+ }
Expression trueExpression = expression.getTrueExpression();
- Expression falseExpression = expression.getFalseExpression();
ClassNode typeOfTrue = findCurrentInstanceOfClass(trueExpression, null);
trueExpression.visit(this);
if (typeOfTrue == null) typeOfTrue = getType(trueExpression);
- // pop if-then-else temporary type info
- typeCheckingContext.popTemporaryTypeInfo();
+ typeCheckingContext.popTemporaryTypeInfo(); // instanceof doesn't apply to false branch
+ Expression falseExpression = expression.getFalseExpression();
falseExpression.visit(this);
ClassNode typeOfFalse = getType(falseExpression);
+
ClassNode resultType;
- // handle instanceof cases
- if (hasInferredReturnType(falseExpression)) {
- typeOfFalse = falseExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
- }
- if (hasInferredReturnType(trueExpression)) {
- typeOfTrue = trueExpression.getNodeMetaData(INFERRED_RETURN_TYPE);
- }
- // TODO consider moving next two statements "up a level", i.e. have just one more widely invoked
- // check but determine no -ve consequences first
- typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
- typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
- if (isNullConstant(trueExpression) || isNullConstant(falseExpression)) {
- BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
- if (enclosingBinaryExpression != null && enclosingBinaryExpression.getRightExpression() == expression) {
- resultType = getType(enclosingBinaryExpression.getLeftExpression());
- } else if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) {
- resultType = OBJECT_TYPE;
- } else if (isNullConstant(trueExpression)) {
- resultType = wrapTypeIfNecessary(typeOfFalse);
- } else {
- resultType = wrapTypeIfNecessary(typeOfTrue);
- }
+ if (isNullConstant(trueExpression) && isNullConstant(falseExpression)) { // GROOVY-5523
+ resultType = checkForTargetType(trueExpression, UNKNOWN_PARAMETER_TYPE);
+ } else if (isNullConstant(trueExpression) || (isEmptyCollection(trueExpression)
+ && isOrImplements(typeOfTrue, typeOfFalse))) { // [] : List/Collection/Iterable
+ resultType = wrapTypeIfNecessary(checkForTargetType(falseExpression, typeOfFalse));
+ } else if (isNullConstant(falseExpression) || (isEmptyCollection(falseExpression)
+ && isOrImplements(typeOfFalse, typeOfTrue))) { // List/Collection/Iterable : []
+ resultType = wrapTypeIfNecessary(checkForTargetType(trueExpression, typeOfTrue));
} else {
- // store type information
+ typeOfFalse = checkForTargetType(falseExpression, typeOfFalse);
+ typeOfTrue = checkForTargetType(trueExpression, typeOfTrue);
resultType = lowestUpperBound(typeOfTrue, typeOfFalse);
}
storeType(expression, resultType);
popAssignmentTracking(oldTracker);
}
+ /**
+ * @param expr true or false branch of ternary expression
+ * @param type the inferred type of {@code expr}
+ */
private ClassNode checkForTargetType(final Expression expr, final ClassNode type) {
- BinaryExpression enclosingBinaryExpression = typeCheckingContext.getEnclosingBinaryExpression();
- if (enclosingBinaryExpression instanceof DeclarationExpression
- && isEmptyCollection(expr) && isAssignment(enclosingBinaryExpression.getOperation().getType())) {
- VariableExpression target = (VariableExpression) enclosingBinaryExpression.getLeftExpression();
- return adjustForTargetType(target.getType(), type);
- }
- if (currentField != null) {
- return adjustForTargetType(currentField.getType(), type);
+ ClassNode sourceType = Optional.ofNullable(getInferredReturnType(expr)).orElse(type);
+
+ ClassNode targetType = null;
+ MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
+ BinaryExpression enclosingExpression = typeCheckingContext.getEnclosingBinaryExpression();
+ if (enclosingExpression != null
+ && isAssignment(enclosingExpression.getOperation().getType())
+ && isTypeSource(expr, enclosingExpression.getRightExpression())) {
+ targetType = getDeclaredOrInferredType(enclosingExpression.getLeftExpression());
+ } else if (enclosingMethod != null
+ && !enclosingMethod.isAbstract()
+ && !enclosingMethod.isVoidMethod()
+ && isTypeSource(expr, enclosingMethod)) {
+ targetType = enclosingMethod.getReturnType();
}
- if (currentProperty != null) {
- return adjustForTargetType(currentProperty.getType(), type);
+
+ if (targetType == null) return sourceType;
+
+ if (expr instanceof ConstructorCallExpression) {
+ inferDiamondType((ConstructorCallExpression) expr, targetType);
+ } else if (!isPrimitiveType(getUnwrapper(targetType))
+ && !OBJECT_TYPE.equals(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());
}
- MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
- if (enclosingMethod != null) {
- return adjustForTargetType(enclosingMethod.getReturnType(), type);
+
+ return sourceType != UNKNOWN_PARAMETER_TYPE ? sourceType : targetType;
+ }
+
+ private static boolean isTypeSource(final Expression expr, final Expression right) {
+ if (right instanceof TernaryExpression) {
+ return isTypeSource(expr, ((TernaryExpression) right).getTrueExpression())
+ || isTypeSource(expr, ((TernaryExpression) right).getFalseExpression());
}
- return type;
+ return expr == right;
}
- private static ClassNode adjustForTargetType(final ClassNode targetType, final ClassNode resultType) {
- if (targetType.isUsingGenerics() && missesGenericsTypes(resultType)) {
- // unchecked assignment within ternary/elvis
- // examples:
- // List<A> list = existingAs ?: []
- // in that case, the inferred type of the RHS is the type of the RHS
- // "completed" with generics type information available in the LHS
- return GenericsUtils.parameterizeType(targetType, resultType.getPlainNodeReference());
+ 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 resultType;
+
+ return returned[0];
}
private static boolean isEmptyCollection(final Expression expr) {
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/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 2bf257c..81dab95 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -391,6 +391,95 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9972
+ void testDiamondInferrenceFromConstructor10() {
+ assertScript '''
+ @groovy.transform.TupleConstructor
+ class C<T> {
+ T p
+ }
+ class D {
+ public String f = 'D#f'
+ }
+ C<D> cd = true ? new C<>(new D()) : new C<>(new D())
+ assert cd.p.f.toLowerCase() == 'd#f'
+ '''
+
+ assertScript '''
+ @groovy.transform.TupleConstructor
+ class C<T> {
+ T p
+ }
+ class D {
+ public String f = 'D#f'
+ }
+ boolean b = false
+ C<D> cd = b ? new C<>(new D()) : (b ? new C<>((D) null) : new C<>(new D()))
+ assert cd.p.f.toLowerCase() == 'd#f'
+ '''
+
+ assertScript '''
+ @groovy.transform.TupleConstructor
+ class C {
+ List<D> list
+ }
+ class D {
+ }
+ List test(C... array) {
+ // old code used "List<D> many" as target for guts of closure
+ List<D> many = array.collectMany { it.list ?: [] }
+ }
+ def result = test(new C(), new C(list:[new D()]))
+ assert result.size() == 1
+ '''
+ }
+
+ // GROOVY-9983
+ void testDiamondInferrenceFromConstructor11() {
+ String types = '''
+ @groovy.transform.TupleConstructor(defaults=false)
+ class A<T> {
+ T p
+ }
+ class B {
+ }
+ class C {
+ static m(A<B> a_of_b) {
+ }
+ }
+ '''
+
+ assertScript types + '''
+ class E extends B {
+ }
+ boolean flag = true
+
+ A<B> v = new A<>(null)
+ A<B> w = new A<>(new B())
+ A<B> x = new A<>(new E())
+ A<B> y = flag ? new A<>(new B()) : new A<>(new B())
+ A<B> z = flag ? new A<>(new B()) : new A<>(new E())
+
+ C.m(new A<>(null))
+ C.m(new A<>(new B()))
+ C.m(new A<>(new E()))
+ C.m(flag ? new A<>(new B()) : new A<>(new B()))
+ C.m(flag ? new A<>(new B()) : new A<>(new E())) // Cannot call m(A<B>) with arguments [A<? extends B>]
+ '''
+
+ /*shouldFailWithMessages types + '''
+ A<B> x = new A<>(new Object())
+ A<B> y = true ? new A<>(new B()) : new A<>(new Object())
+
+ C.m(new A<>(new Object()))
+ C.m(true ? new A<>(new B()) : new A<>(new Object()))
+ ''',
+ 'Cannot assign A<java.lang.Object> to: A<B>',
+ 'Cannot assign A<? extends java.lang.Object> to: A<B>',
+ 'Cannot call C#m(A<B>) with arguments [A<java.lang.Object>]',
+ 'Cannot call C#m(A<B>) with arguments [A<? extends java.lang.Object>]'*/
+ }
+
// GROOVY-10280
void testTypeArgumentPropagation() {
assertScript '''
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index d7c7da6..612b442 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -19,161 +19,217 @@
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
'''
}
+ // GROOVY-10357
+ void testAbstractMethodDefault() {
+ assertScript '''
+ import java.util.function.Function
+
+ abstract class A {
+ abstract long m(Function<Boolean,Integer> f = { Boolean b -> b ? +1 : -1 })
+ }
+
+ def a = new A() {
+ @Override
+ long m(Function<Boolean,Integer> f) {
+ f(true).longValue()
+ }
+ }
+ assert a.m() == 1L
+ '''
+ }
+
// 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
'''
}
-}
+ // GROOVY-10226
+ void testNull4() {
+ assertScript '''
+ class A<T> {
+ }
+ def <T extends A<String>> T test() {
+ final T x = null
+ true ? (T) null : x
+ }
+ assert test() == null
+ '''
+ }
+
+ // GROOVY-10158
+ void testNull5() {
+ assertScript '''
+ class A<T> {
+ }
+ class B<T extends A<String>> {
+ T m() {
+ final T x = null
+ final T y = null
+ ( true ? x : y )
+ }
+ }
+ assert new B<A<String>>().m() == null
+ '''
+ }
+}