GROOVY-11051: convert closure's return value to the method's return type
diff --git a/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java b/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
index ee118e6..194ee8b 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
@@ -23,32 +23,37 @@
import java.io.Serializable;
import java.lang.reflect.Method;
+import static org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType;
+
/**
* This class is a general adapter to adapt a closure to any Java interface.
*/
public class ConvertedClosure extends ConversionHandler implements Serializable {
- private final String methodName;
+
private static final long serialVersionUID = 1162833713450835227L;
+ private final String methodName;
+
/**
- * to create a ConvertedClosure object.
- * @param closure the closure object.
+ * @throws IllegalArgumentException if closure is null
*/
- public ConvertedClosure(Closure closure, String method) {
- super(closure);
- this.methodName = method;
+ public ConvertedClosure(final Closure closure) {
+ this(closure, null);
}
- public ConvertedClosure(Closure closure) {
- this(closure,null);
+ /**
+ * @throws IllegalArgumentException if closure is null
+ */
+ public ConvertedClosure(final Closure closure, final String methodName) {
+ super(closure); this.methodName = methodName;
}
@Override
- public Object invokeCustom(Object proxy, Method method, Object[] args)
- throws Throwable {
- if (methodName!=null && !methodName.equals(method.getName())) return null;
- return ((Closure) getDelegate()).call(args);
+ public Object invokeCustom(final Object proxy, final Method method, final Object[] args) throws Throwable {
+ if (methodName != null && !methodName.equals(method.getName())) return null;
+ Object result = ((Closure)getDelegate()).call(args);
+ if (method.getReturnType()==void.class) return null;
+ result = castToType(result, method.getReturnType());
+ return result;
}
-
}
-
diff --git a/src/test-resources/groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy b/src/test-resources/groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy
index cc3b668..d243917 100644
--- a/src/test-resources/groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy
+++ b/src/test-resources/groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy
@@ -17,7 +17,7 @@
* under the License.
*/
incompatibleAssignment { lhsType, rhsType, expr ->
- if (lhsType == int_TYPE && rhsType==STRING_TYPE) {
+ if (lhsType == int_TYPE && rhsType == STRING_TYPE) {
handled = true
}
-}
\ No newline at end of file
+}
diff --git a/src/test-resources/groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy b/src/test-resources/groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy
index aa19504..0877efd 100644
--- a/src/test-resources/groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy
+++ b/src/test-resources/groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy
@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-incompatibleReturnType { returnStmt, inferredReturnType ->
- if (inferredReturnType==STRING_TYPE) {
+incompatibleReturnType { returnStmt, returnType ->
+ if (returnType == BigDecimal_TYPE || returnType == STRING_TYPE) {
handled = true
}
-}
\ No newline at end of file
+}
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index a72e65b..f33eaef 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -37,16 +37,8 @@
'''
}
- // GROOVY-9079
- void testClosureWithoutArguments3() {
- assertScript '''
- java.util.concurrent.Callable<String> c = { -> return 'foo' }
- assert c() == 'foo'
- '''
- }
-
// GROOVY-10071
- void testClosureWithoutArguments4() {
+ void testClosureWithoutArguments3() {
assertScript '''
def c = { ... zeroOrMore -> return 'foo' + zeroOrMore }
assert c('bar', 'baz') == 'foo[bar, baz]'
@@ -56,7 +48,7 @@
}
// GROOVY-10072, GROOVY-11023
- void testClosureWithoutArguments5() {
+ void testClosureWithoutArguments4() {
assertScript '''
def c = { p = 'foo' -> return p }
assert c('bar') == 'bar'
@@ -70,7 +62,7 @@
}
// GROOVY-10636
- void testClosureWithoutArguments6() {
+ void testClosureWithoutArguments5() {
assertScript '''
def f(Closure<Number>... closures) {
closures*.call().sum()
@@ -207,84 +199,53 @@
'Cannot return value of type X for closure expecting A<java.lang.Number>'
}
- // GROOVY-10128, GROOVY-10306
+ // GROOVY-10792
void testClosureReturnTypeInference8() {
- assertScript '''
- java.util.function.Function<String, Number> x = { s ->
- long n = 1
- return n
+ shouldFailWithMessages '''
+ void proc(Closure<Boolean> c) {
+ boolean result = c().booleanValue()
+ assert !result
}
- assert x.apply('') == 1L
- '''
- assertScript '''
- class C {
- byte p = 1
- void m() {
- byte v = 2
- java.util.function.Supplier<Number> one = { -> p }
- java.util.function.Supplier<Number> two = { -> v }
- assert one.get() == (byte)1
- assert two.get() == (byte)2
- }
+ def list = []
+ proc {
+ list
}
- new C().m()
- '''
- }
-
- // GROOVY-8427
- void testClosureReturnTypeInference9() {
- assertScript '''
- import java.util.function.Consumer
-
- class C {
- static <T> void m(T a, Consumer<T> c) {
- c.accept(a)
- }
- static void main(args) {
- def c = { ->
- int x = 0
- m('') {
- print 'void return'
- }
- }
- c.call()
- }
- }
- '''
+ ''',
+ 'Cannot return value of type java.util.ArrayList<java.lang.Object> for closure expecting java.lang.Boolean'
}
// GROOVY-8202
- void testClosureReturnTypeInference10() {
+ void testClosureReturnTypeInference9() {
assertScript '''
void proc() {
}
String test0(flag) {
- if (flag) {
- 'foo'
- } else {
- proc()
- }
+ if (flag) {
+ 'foo'
+ } else {
+ proc()
+ }
}
String test1(flag) {
- Closure<String> c = { ->
- if (flag) {
- 'bar'
- } else {
- proc()
- null
+ Closure<String> c = { ->
+ if (flag) {
+ 'bar'
+ } else {
+ proc()
+ null
+ }
}
- }
- c.call()
+ c.call()
}
String test2(flag) {
- Closure<String> c = { -> // Cannot assign Closure<Object> to Closure<String>
- if (flag) {
- 'baz'
- } else {
- proc()
+ Closure<String> c = { -> // Cannot assign Closure<Object> to Closure<String>
+ if (flag) {
+ 'baz'
+ } else {
+ proc()
+ }
}
- }
- c.call()
+ c.call()
}
assert test0(true) == 'foo'
@@ -530,7 +491,7 @@
'''
}
- // a case in Grails
+ // from Grails
void testShouldNotThrowClosureSharedVariableError2() {
assertScript '''
class AntPathMatcher {
@@ -589,12 +550,8 @@
// GROOVY-5705
void testNPEWhenCallingClosureFromAField() {
assertScript '''
- import groovy.transform.*
-
class Test {
Closure c = { it }
-
- @TypeChecked
void test() {
c("123")
}
@@ -705,189 +662,6 @@
}
}
- void testSAMType() {
- assertScript '''
- interface I { def m() }
-
- @ASTTest(phase=INSTRUCTION_SELECTION, value={
- assert node.getNodeMetaData(INFERRED_TYPE).name == 'I'
- })
- I i = { 1 }
- assert i.m() == 1
- def x = (I) { 2 }
- assert x.m() == 2
- '''
-
- assertScript '''
- interface I { int m() }
- abstract class A implements I { }
-
- I i = { 1 }
- assert i.m() == 1
- A a = { 2 }
- assert a.m() == 2
- '''
-
- shouldFailWithMessages '''
- interface I {
- String toString()
- }
- I i = { p -> "" }
- ''',
- 'Cannot assign'
-
- shouldFailWithMessages '''
- interface I {
- String toString()
- }
- abstract class A implements I { }
-
- A a = { "" } // implicit parameter
- ''',
- 'Cannot assign'
-
- assertScript '''
- interface I { // non-functional, but every instance extends Object
- boolean equals(Object)
- int m()
- }
- I i = { 1 }
- assert i.m() == 1
- '''
-
- shouldFailWithMessages '''
- interface I {
- boolean equals(Object)
- int m()
- }
- abstract class A implements I { // no abstract methods
- int m() { 1 }
- }
- A a = { 2 }
- ''',
- 'Cannot assign'
- }
-
- // GROOVY-7927
- void testSAMGenericsInAssignment() {
- assertScript '''
- interface SAM<T,R> { R accept(T t); }
- SAM<Integer,Integer> s = { Integer n -> -n }
- assert s.accept(1) == -1
- '''
- }
-
- void testSAMProperty() {
- assertScript '''
- interface SAM { def foo(); }
- class X {
- SAM s
- }
- def x = new X(s:{1})
- assert x.s.foo() == 1
- '''
- }
-
- // GROOVY-7003
- void testSAMProperty2() {
- assertScript '''
- import java.beans.*
-
- class C {
- static PropertyChangeListener listener = { PropertyChangeEvent event ->
- result = "${event.oldValue} -> ${event.newValue}"
- }
- public static result
- }
-
- def event = new PropertyChangeEvent(new Object(), 'foo', 'bar', 'baz')
- C.getListener().propertyChange(event)
- assert C.result == 'bar -> baz'
- '''
- }
-
- void testSAMAttribute() {
- assertScript '''
- interface SAM { def foo(); }
- class X {
- public SAM s
- }
- def x = new X()
- x.s = {1}
- assert x.s.foo() == 1
- x = new X()
- x.@s = {2}
- assert x.s.foo() == 2
- '''
- }
-
- // GROOVY-10254
- void testSAMReturnType() {
- assertScript '''
- interface SAM<T> { T get() }
- SAM<Integer> foo() {
- return { -> 42 }
- }
-
- def result = foo().get()
- assert result == 42
- '''
- }
-
- void testMultipleSAMSignature() {
- assertScript '''
- interface SAM { def foo() }
- def method(SAM a, SAM b) {
- a.foo()
- b.foo()
- }
- method({println 'a'}, {println 'b'})
- '''
- }
-
- void testMultipleSAMSignature2() {
- assertScript '''
- interface SAM { def foo() }
- def method(Object o, SAM a, SAM b) {
- a.foo()
- b.foo()
- }
- method(new Object(), {println 'a'}, {println 'b'})
- '''
- }
-
- void testMultipleSAMMethodWithClosure() {
- assertScript '''
- interface SAM { def foo() }
- def method(SAM a, SAM b) {
- a.foo()
- b.foo()
- }
- def method(Closure a, SAM b) {
- b.foo()
- }
- def called = false
- method({called = true;println 'a'}, {println 'b'})
- assert !called
- '''
- }
-
- void testMultipleSAMMethodWithClosureInverted() {
- assertScript '''
- interface SAM { def foo() }
- def method(SAM a, SAM b) {
- a.foo()
- b.foo()
- }
- def method(SAM a, Closure b) {
- a.foo()
- }
- def called = false
- method({println 'a'}, {called=true;println 'b'})
- assert !called
- '''
- }
-
// GROOVY-6238
void testDirectMethodCallOnClosureExpression() {
assertScript '''
@@ -962,20 +736,6 @@
'''
}
- void testParameterlessClosureToSAMTypeArgumentCoercion() {
- assertScript '''
- interface SamType {
- int sam()
- }
-
- int foo(SamType samt) {
- samt.sam()
- }
-
- assert foo { -> 1 } == 1
- '''
- }
-
// GROOVY-9558
void testPutAtClosureDelegateProperty() {
assertScript '''
@@ -1047,26 +807,4 @@
'Cannot assign value of type java.lang.Class<java.lang.Integer> to variable of type int',
"named param 'bar' has type 'java.lang.Class<java.lang.Number>' but expected 'java.lang.Number'"
}
-
- // GROOVY-10905
- void testImplicitArgClosureMatchesSamMethodWithOneArg() {
- assertScript '''
- def method1(java.util.function.IntUnaryOperator unary) { '1a' }
- def method1(java.util.function.IntBinaryOperator binary) { '1b' }
- assert method1{ x -> } == '1a'
- assert method1{ x, y -> } == '1b'
- assert method1{ } == '1a'
- '''
- }
-
- // GROOVY-10905
- void testImplicitArgClosureMatchesSamMethodWithZeroArgs() {
- assertScript '''
- def method2(java.util.function.IntSupplier supplier) { '2a' }
- def method2(java.util.function.IntBinaryOperator binary) { '2b' }
- assert method2{ -> } == '2a'
- assert method2{ x, y -> } == '2b'
- assert method2{ } == '2a'
- '''
- }
}
diff --git a/src/test/groovy/transform/stc/CoercionSTCTest.groovy b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
index 87ecdd8..b07cc01 100644
--- a/src/test/groovy/transform/stc/CoercionSTCTest.groovy
+++ b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
@@ -18,11 +18,20 @@
*/
package groovy.transform.stc
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+
/**
* Unit tests for static type checking : coercions.
*/
class CoercionSTCTest extends StaticTypeCheckingTestCase {
+ @Override
+ void configure() {
+ config.addCompilationCustomizers(
+ new ImportCustomizer().addStarImports('java.util.function')
+ )
+ }
+
void testCoerceToArray() {
assertScript '''
try {
@@ -128,45 +137,216 @@
'''
}
+ void testCoerceToFunctionalInterface1() {
+ String sam = '@FunctionalInterface interface SAM { def foo() }'
+
+ assertScript sam + '''
+ def test(SAM a, SAM b) {
+ '' + a.foo() + b.foo()
+ }
+ String result = test({'a'}, {'b'})
+ assert result == 'ab'
+ '''
+
+ assertScript sam + '''
+ def test(Object o, SAM a, SAM b) {
+ '' + a.foo() + b.foo()
+ }
+ String result = test(new Object(), {'a'}, {'b'})
+ assert result == 'ab'
+ '''
+
+ assertScript sam + '''
+ def test(SAM a, SAM b) {
+ '' + a.foo() + b.foo()
+ }
+ def test(Closure a, SAM b) {
+ b.foo()
+ }
+ String result = test({'a'}, {'b'})
+ assert result == 'b'
+ '''
+
+ assertScript sam + '''
+ def test(SAM a, SAM b) {
+ '' + a.foo() + b.foo()
+ }
+ def test(SAM a, Closure b) {
+ a.foo()
+ }
+ String result = test({'a'}, {'b'})
+ assert result == 'a'
+ '''
+ }
+
+ // GROOVY-10254
+ void testCoerceToFunctionalInterface2() {
+ assertScript '''
+ @FunctionalInterface
+ interface SAM<T> { T get() }
+
+ SAM<Integer> foo() {
+ return { -> 42 }
+ }
+
+ def result = foo().get()
+ assert result == 42
+ '''
+ }
+
// GROOVY-10277
- void testCoerceToFunctionalInterface() {
- assertScript '''import java.util.function.*
+ void testCoerceToFunctionalInterface3() {
+ assertScript '''
Consumer<Number> c = { n -> }
Supplier<Number> s = { -> 42 }
Predicate<Number> p = { n -> 42 }
'''
- assertScript '''import java.util.function.*
+ assertScript '''
def c = (Consumer<Number>) { n -> }
def s = (Supplier<Number>) { -> 42 }
def p = (Predicate<Number>) { n -> 42 }
'''
- assertScript '''import java.util.function.*
+ assertScript '''
def c = { n -> } as Consumer<Number>
def s = { -> 42 } as Supplier<Number>
def p = { n -> 42 } as Predicate<Number>
'''
- shouldFailWithMessages '''import java.util.function.*
+ shouldFailWithMessages '''
def s = (Supplier<Number>) { -> false }
''',
'Cannot return value of type boolean for closure expecting java.lang.Number'
- shouldFailWithMessages '''import java.util.function.*
+ shouldFailWithMessages '''
def s = { -> false } as Supplier<Number>
''',
'Cannot return value of type boolean for closure expecting java.lang.Number'
- shouldFailWithMessages '''import java.util.function.*
+ shouldFailWithMessages '''
def s = (() -> ['']) as Supplier<Number>
''',
'Cannot return value of type java.util.ArrayList<java.lang.String> for lambda expecting java.lang.Number'
}
+ void testCoerceToFunctionalInterface4() {
+ assertScript '''
+ interface I { def m() }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION, value={
+ assert node.getNodeMetaData(INFERRED_TYPE).name == 'I'
+ })
+ I i = { 1 }
+ assert i.m() == 1
+ def x = (I) { 2 }
+ assert x.m() == 2
+ '''
+
+ assertScript '''
+ interface I { int m() }
+ abstract class A implements I { }
+
+ I i = { 1 }
+ assert i.m() == 1
+ A a = { 2 }
+ assert a.m() == 2
+ '''
+
+ shouldFailWithMessages '''
+ interface I {
+ String toString()
+ }
+ I i = { p -> "" }
+ ''',
+ 'Cannot assign'
+
+ shouldFailWithMessages '''
+ interface I {
+ String toString()
+ }
+ abstract class A implements I { }
+
+ A a = { "" } // implicit parameter
+ ''',
+ 'Cannot assign'
+
+ assertScript '''
+ interface I { // non-functional, but every instance extends Object
+ boolean equals(Object)
+ int m()
+ }
+ I i = { 1 }
+ assert i.m() == 1
+ '''
+
+ shouldFailWithMessages '''
+ interface I {
+ boolean equals(Object)
+ int m()
+ }
+ abstract class A implements I { // no abstract methods
+ int m() { 1 }
+ }
+ A a = { 2 }
+ ''',
+ 'Cannot assign'
+ }
+
+ // GROOVY-7927
+ void testCoerceToFunctionalInterface5() {
+ assertScript '''
+ interface SAM<T,R> { R accept(T t); }
+ SAM<Integer,Integer> s = { Integer n -> -n }
+ assert s.accept(1) == -1
+ '''
+ }
+
+ void testCoerceToFunctionalInterface6() {
+ assertScript '''
+ interface SAM { def foo(); }
+ class X {
+ public SAM s
+ }
+ def x = new X()
+ x.s = {1}
+ assert x.s.foo() == 1
+ x = new X()
+ x.@s = {2}
+ assert x.s.foo() == 2
+ '''
+ }
+
+ void testCoerceToFunctionalInterface7() {
+ assertScript '''
+ interface SAM { def foo(); }
+ class X {
+ SAM s
+ }
+ def x = new X(s:{1})
+ assert x.s.foo() == 1
+ '''
+ }
+
+ // GROOVY-7003
+ void testCoerceToFunctionalInterface8() {
+ assertScript '''import java.beans.*
+ class C {
+ static PropertyChangeListener listener = { PropertyChangeEvent event ->
+ result = "${event.oldValue} -> ${event.newValue}"
+ }
+ public static result
+ }
+
+ def event = new PropertyChangeEvent(new Object(), 'foo', 'bar', 'baz')
+ C.getListener().propertyChange(event)
+ assert C.result == 'bar -> baz'
+ '''
+ }
+
// GROOVY-8045
- void testCoerceToFunctionalInterface2() {
- assertScript '''import java.util.function.*
+ void testCoerceToFunctionalInterface9() {
+ assertScript '''
def f(Supplier<Integer>... suppliers) {
suppliers*.get().sum()
}
@@ -174,4 +354,116 @@
assert result == 3
'''
}
+
+ // GROOVY-8168
+ void testCoerceToFunctionalInterface10() {
+ String sam = '''
+ @FunctionalInterface
+ interface Operation {
+ double calculate(int i)
+ }
+ '''
+
+ assertScript sam + '''
+ Operation operation = { return 1.0d }
+ def result = operation.calculate(2)
+ assert result == 1.0d
+ '''
+
+ shouldFailWithMessages sam + '''
+ Operation operation = { return 1.0; }
+ ''',
+ 'Cannot return value of type java.math.BigDecimal for closure expecting double'
+ }
+
+ // GROOVY-8427
+ void testCoerceToFunctionalInterface11() {
+ assertScript '''
+ def <T> void m(T a, Consumer<T> c) {
+ c.accept(a)
+ }
+
+ def c = { ->
+ int x = 0
+ m('') {
+ print 'void return'
+ }
+ }
+ c.call()
+ '''
+ }
+
+ // GROOVY-9079
+ void testCoerceToFunctionalInterface12() {
+ assertScript '''import java.util.concurrent.Callable
+ Callable<String> c = { -> return 'foo' }
+ assert c() == 'foo'
+ '''
+ }
+
+ // GROOVY-10128, GROOVY-10306
+ void testCoerceToFunctionalInterface13() {
+ assertScript '''
+ Function<String, Number> x = { s ->
+ long n = 1
+ return n
+ }
+ assert x.apply('') == 1L
+ '''
+
+ assertScript '''
+ class C {
+ byte p = 1
+ void m() {
+ byte v = 2
+ Supplier<Number> one = { -> p }
+ Supplier<Number> two = { -> v }
+ assert one.get() == (byte)1
+ assert two.get() == (byte)2
+ }
+ }
+ new C().m()
+ '''
+ }
+
+ // GROOVY-10792
+ void testCoerceToFunctionalInterface14() {
+ assertScript '''
+ @Grab('org.awaitility:awaitility-groovy:4.2.0')
+ import static org.awaitility.Awaitility.await
+
+ List<String> strings = ['x']
+ await().until { -> strings }
+ '''
+ }
+
+ // GROOVY-10905
+ void testCoerceToFunctionalInterface15() {
+ assertScript '''
+ def method(IntUnaryOperator unary) { '1a' }
+ def method(IntBinaryOperator binary) { '1b' }
+
+ assert method{ x -> } == '1a'
+ assert method{ x, y -> } == '1b'
+ assert method{ } == '1a'
+ '''
+
+ assertScript '''
+ def method(IntSupplier supplier) { '2a' }
+ def method(IntBinaryOperator binary) { '2b' }
+
+ assert method{ -> } == '2a'
+ assert method{ x, y -> } == '2b'
+ assert method{ } == '2a'
+ '''
+ }
+
+ // GROOVY-11051
+ void testCoerceToFunctionalInterface16() {
+ assertScript '''import java.util.concurrent.atomic.AtomicReference
+ def opt = new AtomicReference<Object>(null)
+ .stream().filter { it.get() }.findAny()
+ assert opt.isEmpty()
+ '''
+ }
}
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 40bf98d..0d3eead 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -245,6 +245,17 @@
'''
}
+ @Test // class::instanceMethod -- GROOVY-11051
+ void testPredicateCI2() {
+ [['null','Empty'],['new Object()','Present']].each { value, which ->
+ assertScript """import java.util.concurrent.atomic.AtomicReference
+ def opt = new AtomicReference<Object>($value).stream()
+ .filter(AtomicReference::get).findFirst()
+ assert opt.is${which}()
+ """
+ }
+ }
+
@Test // class::instanceMethod -- GROOVY-10791
void testBiConsumerCI() {
assertScript shell, '''
diff --git a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
index 8c7b1ab..78088f6 100644
--- a/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
+++ b/src/test/groovy/transform/stc/TypeCheckingExtensionsTest.groovy
@@ -323,21 +323,6 @@
'Method [three] with matching argument found: [2, class java.util.Date]'
}
- void testIncompatibleAssignment() {
- extension = null
- shouldFailWithMessages '''
- int x = 'foo'
- ''',
- 'Cannot assign value of type java.lang.String to variable of type int'
-
- extension = 'groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy'
- assertScript '''
- try {
- int x = 'foo'
- } catch (e) {}
- '''
- }
-
void testBinaryOperatorNotFound() {
extension = null
shouldFailWithMessages '''
@@ -484,19 +469,53 @@
}
}
- void testIncompatibleReturnType() {
- extension = null
- shouldFailWithMessages '''
- Date foo() { '1' }
- true
- ''',
- 'Cannot return value of type'
+ void testIncompatibleAssignment() {
+ String source = '''
+ int x = 'x'
+ assert x == 120
+ '''
- extension = 'groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy'
- assertScript '''
+ extension = null
+ shouldFailWithMessages source,
+ 'Cannot assign value of type java.lang.String to variable of type int'
+
+ extension = 'groovy/transform/stc/IncompatibleAssignmentTestExtension.groovy'
+ assertScript source
+ }
+
+ void testIncompatibleReturnType() {
+ String source = '''
Date foo() { '1' }
true
'''
+
+ extension = null
+ shouldFailWithMessages source,
+ 'Cannot return value of type java.lang.String for method returning java.util.Date'
+
+ extension = 'groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy'
+ assertScript source
+ }
+
+ // GROOVY-8168
+ void testIncompatibleReturnType2() {
+ String source = '''
+ @FunctionalInterface
+ interface Operation {
+ double calculate(int i)
+ }
+
+ Operation operation = { i -> return 1.0 }
+ def result = operation.calculate(2)
+ assert result == 1.0d
+ '''
+
+ extension = null
+ shouldFailWithMessages source,
+ 'Cannot return value of type java.math.BigDecimal for closure expecting double'
+
+ extension = 'groovy/transform/stc/IncompatibleReturnTypeTestExtension.groovy'
+ assertScript source
}
void testPrecompiledExtension() {