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
'''
}
}
-