blob: f159e0f276495ad231ac391ba01cea65fe6249f6 [file] [log] [blame]
/*
* 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.transform.stc
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import static org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder.withConfig
/**
* Unit tests for static type checking : method calls.
*/
class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
@Override
protected void configure() {
withConfig(config) {
imports {
alias 'A', 'groovy.transform.stc.MethodCallsSTCTest.MyMethodCallTestClass'
alias 'B', 'groovy.transform.stc.MethodCallsSTCTest.MyMethodCallTestClass2'
alias 'C', 'groovy.transform.stc.MethodCallsSTCTest.MyMethodCallTestClass3'
}
}
}
void testMethodCallOnInstance() {
assertScript '''
A a = new A()
assert a.add(1,1)==2
'''
}
void testBestChoiceMethodCallOnInstance() {
assertScript '''
A a = new A()
assert a.add(1d,1d)==3
'''
}
void testMissingInstanceMethod() {
shouldFailWithMessages '''
A a = new A()
assert a.foo(1,1)==2
''', 'Cannot find matching method'
}
void testMethodCallOnInstanceWithVarArgs() {
assertScript '''
A a = new A()
assert a.sum(1,1,2)==4
'''
}
void testMethodCallOnInstanceWithVarArgs2() {
assertScript '''
A a = new A()
int[] arr = [1,1,2]
assert a.sum(arr)==4
'''
}
void testStaticMethodCall() {
assertScript '''
String echo = A.echo 'echo'
assert echo == 'echo'
'''
}
void testMissingStaticMethod() {
shouldFailWithMessages '''
A.missing 'echo'
''', 'Cannot find matching method'
}
void testStaticMethodWithVarArgs() {
assertScript '''
int mul = A.mul([1,2,3] as int[])
assert mul == 6
'''
}
void testStaticMethodWithVarArgs2() {
assertScript '''
int mul = A.mul(1,2,3)
assert mul == 6
'''
}
void testStaticMethodCallWithInheritance() {
assertScript '''
String echo = B.echo 'echo'
assert echo == 'echo'
'''
}
void testStaticMethodCallThroughInstance() {
assertScript '''
A a = new A()
String echo = a.echo 'echo'
assert echo == 'echo'
'''
}
void testStaticMethodCallOnJDK() {
assertScript '''
int[] arr = [3,2,1]
Arrays.sort(arr)
assert arr == [1,2,3] as int[]
'''
}
void testStaticMethodCallOnJDK2() {
assertScript '''
String[] arr = ['3','2','1']
Arrays.sort(arr)
assert arr == ['1','2','3'] as String[]
'''
}
void testStaticMethodCallOnJDK3() {
assertScript '''
List arr = ['3','2','1']
Collections.sort(arr)
assert arr == ['1','2','3']
'''
}
void testStaticMethodCallOnJDK4() {
assertScript '''
List<String> arr = ['3','2','1']
Collections.sort(arr)
assert arr == ['1','2','3']
'''
}
void testPlusStaticMethodCall() {
assertScript '''
static int foo() { 1 }
assert 1+foo() == 2
'''
}
void testExplicitTargetMethodWithCast() {
assertScript '''
String foo(String str) { 'STRING' }
String foo(Object o) { 'OBJECT' }
assert foo('call') == 'STRING'
assert foo((Object)'call') == 'OBJECT'
'''
}
void testGenericMethodCall() {
assertScript '''
C c = new C()
String[] args = ['a','b','c']
assert c.identity(args) == args
'''
}
void testGenericMethodCallWithVarArg() {
assertScript '''
C c = new C()
assert c.identity('a','b','c') == ['a','b','c']
'''
}
void testGenericMethodCallWithVarArgAndSingleArg() {
assertScript '''
C c = new C()
assert c.identity('a') == ['a']
'''
}
void testGenericMethodCallWithVarArgAndNoArg() {
assertScript '''
C c = new C()
assert c.identity() == []
'''
}
void testGenericMethodCall2() {
assertScript '''
B c = new B<String>()
String[] args = ['a','b','c']
assert c.identity(args) == args
'''
}
void testGenericMethodCall3() {
shouldFailWithMessages '''
B c = new B<Integer>()
String[] args = ['a','b','c']
assert c.identity(args) == args
''', 'Cannot call groovy.transform.stc.MethodCallsSTCTest$MyMethodCallTestClass2#identity(java.lang.Integer[]) with arguments [java.lang.String[]]'
}
// GROOVY-8909
void testGenericMethodCall4() {
assertScript '''
void m(List<Object> list) {
assert list.size() == 3
}
m([1,2,3])
'''
// no coercion like assignment
shouldFailWithMessages '''
void m(Set<Integer> set) {
}
m([1,2,3,3])
''', 'm(java.util.List<java.lang.Integer>). Please check if the declared type is correct and if the method exists.'
}
// GROOVY-7106, GROOVY-7274, GROOVY-9844
void testGenericMethodCall5() {
assertScript '''
void m(Map<CharSequence,Number> map) {
assert map.size() == 3
assert map['a'] == 1
assert 'z' !in map
}
m([a:1,b:2,c:3])
'''
assertScript '''
void m(Map<String,Object> map) {
}
m([d:new Date(), i:1, s:""])
'''
assertScript '''
void m(Map<String,Object> map) {
}
['x'].each {
m([(it): it.toLowerCase()])
}
'''
}
void testNullSafeCall() {
assertScript '''
String str = null
assert str?.toString() == null
'''
}
void testCallToSuper() {
assertScript '''
class Foo {
int foo() { 1 }
}
class Bar extends Foo {
int foo() { super.foo() }
}
def bar = new Bar()
assert bar.foo() == 1
'''
}
// GROOVY-10494
void testCallToSuperDefault() {
assertScript '''
interface I<T> {
default m(T t) {
return t
}
}
class C implements I<String> {
@Override m(String s) {
I.super.m(s)
}
}
String result = new C().m('works')
assert result == 'works'
'''
shouldFailWithMessages '''
interface I<T> {
default void m(T t) {
}
}
class C implements I<String> {
@Override void m(String s) {
super.m(s)
}
}
''',
'Default method m(T) requires qualified super'
}
void testMethodCallFromSuperOwner() {
assertScript '''
class Child extends groovy.transform.stc.MethodCallsSTCTest.GroovyPage {
void foo() {
createTagBody(1) { ->
printHtmlPart(2)
}
}
}
new Child()
'''
}
void testCallToPrivateInnerClassMethod() {
assertScript '''
class Outer {
static class Inner {
private static void foo() {}
}
static main(args) { Inner.foo() }
}
'''
}
void testCallToPrivateOuterClassMethod() {
assertScript '''
class Outer {
private static void foo() {}
static class Inner {
private static void bar() { Outer.foo() }
}
}
new Outer.Inner()
'''
}
void testCallToPrivateInnerClassConstant() {
assertScript '''
class Outer {
static class Inner {
private static int foo = 42
}
static main(args) { Inner.foo }
}
'''
}
void testCallToPrivateOuterClassConstant() {
assertScript '''
class Outer {
private static int foo = 42
static class Inner {
private static void bar() { Outer.foo }
}
}
new Outer.Inner()
'''
}
void testReferenceToInaccessiblePrivateMethod() {
shouldFail(MultipleCompilationErrorsException) {
assertScript '''
class Main {
static main(args) { Peer.foo() }
}
class Peer {
private static void foo() {}
}
'''
}
}
// GROOVY-6647
void testReferenceToInaccessiblePrivateConstructor() {
shouldFailWithMessages '''
class Main {
private Main() {}
}
class Peer {
def foo() { new Main() }
}
''', '[Static type checking] - Cannot find matching method Main#<init>()'
}
// GROOVY-8509
void testCallProtectedFromClassInSamePackage() {
assertScript '''
class Foo {
protected Foo() {}
protected int m() { 123 }
}
class Bar {
int test() {
new Foo().m()
}
}
assert new Bar().test() == 123
'''
}
// GROOVY-7862
void testCallProtectedMethodFromInnerClassInSeparatePackage() {
assertScript '''
import groovy.transform.stc.MethodCallsSTCTest.BaseWithProtected as Foo
class Bar extends Foo {
class Baz {
int test() {
m()
}
}
int test() {
new Baz().test()
}
}
assert new Bar().test() == 1
'''
}
// GROOVY-7063
void testCallProtectedMethodFromSubclassClosureInDifferentPackage() {
assertScript '''
import groovy.transform.stc.MethodCallsSTCTest.BaseWithProtected as Foo
class Bar extends Foo {
int baz() {
def c = {
m()
}
c.call()
}
}
def bar = new Bar()
assert bar.baz() == 1
'''
}
// GROOVY-7264
void testCallProtectedMethodWithGenericTypes() {
assertScript '''
class Foo<T> {
protected boolean m(T t) {
true
}
}
class Bar extends Foo<Integer> {
int baz() {
def c = {
m(123)
}
c.call() ? 1 : 0
}
}
def bar = new Bar()
assert bar.baz() == 1
'''
}
// GROOVY-5175
void testCallMethodAcceptingArrayWithNull() {
assertClass '''
class Main {
def bar(String[] s) {
}
def foo() {
bar(null)
}
}
'''
}
// GROOVY-5175
void testCallMethodWithNull() {
assertClass '''
class Main {
def bar(Date date) {
}
def foo() {
bar(null)
}
}
'''
}
// GROOVY-5175
void testCallMethodWithNullAndAnotherParameter() {
assertClass '''
class Main {
def bar(Date date1, Date date2) {
}
def foo() {
bar(null, new Date())
}
}
'''
}
// GROOVY-5175
void testAmbiguousCallMethodWithNullAndAnotherParameter() {
shouldFailWithMessages '''
class Main {
def bar(Date date1, Date date2) {
}
def bar(String o, Date date2) {
}
def foo() {
bar(null, new Date())
}
}
''', 'Reference to method is ambiguous'
}
// GROOVY-5175
void testDisambiguateCallMethodWithNullAndAnotherParameter() {
assertClass '''
class Test {
def bar(Date date1, Date date2) {
}
def bar(String o, Date date2) {
}
def foo() {
bar((Date)null, new Date())
}
}
'''
}
void testMethodCallWithDefaultParams() {
assertScript '''
class Support {
Support(String name, String val, List arg=null, Set set = null, Date suffix = new Date()) {
"$name$val$suffix"
}
}
new Support(null, null, null, null)
'''
}
void testMethodCallArgumentUsingInstanceOf() {
assertScript '''
void foo(String str) { 'String' }
def o
if (o instanceof String) {
foo(o)
}
'''
}
void testShouldFindStaticMethod() {
assertScript '''
static String foo(String s) {
'String'
}
foo('String')
'''
}
void testShouldFailWithNoMatchingMethod() {
shouldFailWithMessages '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each { foo(it) }
''', 'Cannot find matching method'
}
void testShouldNotFailThanksToInstanceOfChecks() {
assertScript '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each {
if (it instanceof String) {
foo((String)it)
} else if (it instanceof Boolean) {
foo((Boolean)it)
} else if (it instanceof Integer) {
foo((Integer)it)
}
}
'''
}
void testShouldNotFailThanksToInstanceOfChecksAndWithoutExplicitCasts() {
assertScript '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each {
if (it instanceof String) {
foo(it)
} else if (it instanceof Boolean) {
foo(it)
} else if (it instanceof Integer) {
foo(it)
}
}
'''
}
void testShouldNotFailThanksToInstanceOfChecksAndWithoutExplicitCasts2() {
assertScript '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each { argument ->
if (argument instanceof String) {
foo(argument)
} else if (argument instanceof Boolean) {
foo(argument)
} else if (argument instanceof Integer) {
foo(argument)
}
}
'''
}
void testShouldFailWithMultiplePossibleMethods() {
shouldFailWithMessages '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each {
if (it instanceof String || it instanceof Boolean || it instanceof Integer) {
foo(it)
}
}
''', 'Reference to method is ambiguous'
}
void testShouldFailWithMultiplePossibleMethods2() {
shouldFailWithMessages '''
static String foo(String s) {
'String'
}
static String foo(Integer s) {
'Integer'
}
static String foo(Boolean s) {
'Boolean'
}
['foo',123,true].each { argument ->
if (argument instanceof String || argument instanceof Boolean || argument instanceof Integer) {
foo(argument)
}
}
''', 'Reference to method is ambiguous'
}
// GROOVY-5703
void testShouldNotConvertStringToStringArray() {
assertScript '''
int printMsgs(String ... msgs) {
int i = 0
for(String s : msgs) { i++ }
i
}
assert printMsgs('foo') == 1
assert printMsgs('foo','bar') == 2
'''
}
// GROOVY-5780
void testShouldNotConvertGStringToStringArray() {
assertScript '''
int printMsgs(String ... msgs) {
int i = 0
for(String s : msgs) { i++ }
i
}
assert printMsgs("f${'o'}o") == 1
assert printMsgs("${'foo'}","${'bar'}") == 2
'''
}
void testInstanceOfOnExplicitParameter() {
assertScript '''
1.with { obj ->
if (obj instanceof String) {
obj.toUpperCase()
}
}
'''
}
void testSAMWithExplicitParameter1() {
assertScript '''
public interface SAM {
boolean run(String var1, Thread th);
}
static boolean foo(SAM sam) {
sam.run("foo", new Thread())
}
static def callSAM() {
foo { str, th ->
str.toUpperCase().equals(th.getName())
}
}
'''
}
// GROOVY-8241
void testSAMWithExplicitParameter2() {
assertScript '''
static boolean foo(java.util.function.Predicate<? super String> p) {
p.test('bar')
}
foo { it -> it.toUpperCase(); return true }
'''
}
// GROOVY-7061
void testSAMWithExplicitParameter3() {
assertScript '''
void test() {
List<Integer> nums = [1, 2, 3, -2, -5, 6]
Collections.sort(nums, { a, b -> a.abs() <=> b.abs() })
}
test()
'''
}
// GROOVY-7061
void testSAMWithExplicitParameter4() {
assertScript '''
def foo(List<String> strings) {
strings.stream().filter { s -> s.length() < 10 }.toArray()
}
def words = ["orange", "sit", "test", "flabbergasted", "honorific"]
foo(words)
'''
}
void testShouldFailBecauseVariableIsReassigned() {
shouldFailWithMessages '''
static String foo(String s) {
'String'
}
def it
if (it instanceof String) {
it = new Date()
foo(it)
}
''', 'foo(java.util.Date)'
}
void testShouldNotFailEvenIfVariableIsReassigned() {
assertScript '''
static String foo(int val) {
'int'
}
def it
if (it instanceof String) {
it = 123
foo(it)
}
'''
}
void testShouldNotFailEvenIfVariableIsReassignedAndInstanceOfIsEmbed() {
assertScript '''
static String foo(int val) {
'int'
}
static String foo(Date val) {
'Date'
}
def it
if (it instanceof String) {
it = 123
foo(it)
if (it instanceof Date) {
foo(it)
}
}
'''
}
void testOneDefaultParam() {
assertScript '''
String m(String val = 'hello') {
return val.toUpperCase()
}
assert m() == 'HELLO'
assert m('bye') == 'BYE'
'''
}
void testOneDefaultParamWithWrongArgType() {
shouldFailWithMessages '''
String m(String val = 'hello') {
return val.toUpperCase()
}
assert m(123) == 'HELLO'
''', '#m(int)'
}
void testOneDefaultParamAndOneWithout() {
assertScript '''
String m(String val = 'hello', int append) {
return val.toUpperCase() + append
}
assert m(1) == 'HELLO1'
assert m('bye',2) == 'BYE2'
'''
}
void testOneDefaultParamAndOneWithoutWithWrongArgType() {
shouldFailWithMessages '''
String m(String val = 'hello', int append) {
return val.toUpperCase() + append
}
m('test', new Object())
''', 'm(java.lang.String, java.lang.Object)'
}
void testMultipleDefaultArgs() {
assertScript '''
String m(String first = 'first', String second, String third = 'third') {
return first.toUpperCase() + ' ' + second.toUpperCase() + ' ' + third.toUpperCase()
}
assert m('hello') == 'FIRST HELLO THIRD'
'''
}
void testMultipleDefaultArgsWithMixedTypes() {
assertScript '''
String m(String first = 'first', int second, String third = 'third') {
return first.toUpperCase() + ' ' + second + ' ' + third.toUpperCase()
}
assert m(123) == 'FIRST 123 THIRD'
assert m('f',123) == 'F 123 THIRD'
assert m('f',123,'s') == 'F 123 S'
'''
}
void testMultipleDefaultArgsWithMixedTypesAndTooManyArgs() {
shouldFailWithMessages '''
String m(String first = 'first', int second, String third = 'third') {
return first.toUpperCase() + ' ' + second + ' ' + third.toUpperCase()
}
m('f',123,'s', 'too many args')
''', '#m(java.lang.String, int, java.lang.String, java.lang.String)'
}
void testMultipleDefaultArgsWithMixedTypesAndWrongType() {
shouldFailWithMessages '''
String m(String first = 'first', int second, String third = 'third') {
return first.toUpperCase() + ' ' + second + ' ' + third.toUpperCase()
}
m('hello') // no value set for "second"
''', '#m(java.lang.String)'
}
void testShouldNotFailWithAmbiguousMethodSelection() {
assertScript '''
StringBuffer sb = new StringBuffer()
sb.append('foo')
'''
}
void testShouldBeAbleToCallMethodUsingDoubleWithDoubleFloatLongIntShortOrByte() {
assertScript '''
double square(double x) { x*x }
assert square(2.0d) == 4.0d
assert square(2.0f) == 4.0d
assert square(2L) == 4.0d
assert square(2) == 4.0d
assert square((short)2) == 4.0d
assert square((byte)2) == 4.0d
'''
}
void testShouldNotBeAbleToCallMethodUsingFloatWithDouble() {
shouldFailWithMessages '''
float square(float x) { x*x }
assert square(2.0d) == 4.0d
''', '#square(double)'
}
void testShouldNotBeAbleToCallMethodUsingLongWithFloatOrDouble() {
shouldFailWithMessages '''
float square(long x) { x*x }
assert square(2.0d) == 4.0d
assert square(2.0f) == 4.0d
''', '#square(double)', '#square(float)'
}
void testShouldNotAllowMethodCallFromStaticContext() {
shouldFailWithMessages '''
class A {
void instanceMethod() {}
static void staticMethod() {
instanceMethod() // calling instance method from static context
}
}
A.staticMethod()
''', 'Non-static method A#instanceMethod cannot be called from static context'
}
void testShouldNotAllowMethodCallFromStaticConstructor() {
shouldFailWithMessages '''
class A {
void instanceMethod() {}
static {
instanceMethod() // calling instance method from static context
}
}
new A()
''', 'Non-static method A#instanceMethod cannot be called from static context'
}
void testShouldNotAllowMethodCallFromStaticField() {
shouldFailWithMessages '''
class A {
boolean instanceMethod() {}
static FOO = instanceMethod()
}
new A()
''', 'Non-static method A#instanceMethod cannot be called from static context'
}
// GROOVY-5495
void testShouldFindMethodFromSuperInterface() {
assertScript '''
class ClassUnderTest {
void methodFromString(SecondInterface si) {
si.methodFromSecondInterface();
si.methodFromFirstInterface();
}
}
interface FirstInterface {
void methodFromFirstInterface();
}
interface SecondInterface extends FirstInterface {
void methodFromSecondInterface();
}
new ClassUnderTest()
'''
}
void testShouldNotBeAmbiguousCall() {
assertScript '''
(0..10).find { int x -> x < 5 }
'''
}
void testEqualsCalledOnInterface() {
assertScript '''
Serializable ser = (Serializable) new Integer(1)
if (ser !=null) { // ser.equals(null)
println 'ok'
int hash = ser.hashCode()
String str = ser.toString()
try {
ser.notify()
} catch (e) {}
try {
ser.notifyAll()
} catch (e) {}
try {
ser.wait()
} catch (e) {}
}
'''
}
// GROOVY-5534
void testSafeDereference() {
assertScript '''
def foo() {
File bar
bar?.name
}
assert foo() == null
'''
}
// GROOVY-5540
void testChoosePublicMethodInHierarchy() {
assertScript '''
import groovy.transform.stc.MethodCallsSTCTest.Child2 as C2
class A {
int delegate() {
@ASTTest(phase=INSTRUCTION_SELECTION, value={
def md = node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert md.declaringClass.nameWithoutPackage == 'MethodCallsSTCTest$ChildWithPublic'
})
int res = new C2().m()
res
}
}
assert new A().delegate() == 2
'''
}
// GROOVY-5580
void testGetNameAsPropertyFromSuperInterface() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
String foo(Lower impl) {
impl.name // getName() called with the property notation
}
assert foo({ 'bar' } as Lower) == 'bar'
'''
}
void testGetNameAsPropertyFromSuperInterfaceUsingConcreteImpl() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
class Foo implements Lower { String getName() { 'bar' } }
String foo(Foo impl) {
impl.name // getName() called with the property notation
}
assert foo(new Foo()) == 'bar'
'''
}
void testGetNameAsPropertyFromSuperInterfaceUsingConcreteImplSubclass() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
class Foo implements Lower { String getName() { 'bar' } }
class Bar extends Foo {}
String foo(Bar impl) {
impl.name // getName() called with the property notation
}
assert foo(new Bar()) == 'bar'
'''
}
void testIsGetterAsPropertyFromSuperInterface() {
assertScript '''
interface Upper { boolean isBar() }
interface Lower extends Upper {}
boolean foo(Lower impl) {
impl.bar // isBar() called with the property notation
}
assert foo({ true } as Lower)
'''
}
void testIsGetterAsPropertyFromSuperInterfaceUsingConcreteImpl() {
assertScript '''
interface Upper { boolean isBar() }
interface Lower extends Upper {}
class Foo implements Lower { boolean isBar() { true } }
boolean foo(Foo impl) {
impl.bar // isBar() called with the property notation
}
assert foo(new Foo())
'''
}
void testIsGetterAsPropertyFromSuperInterfaceUsingConcreteImplSubclass() {
assertScript '''
interface Upper { boolean isBar() }
interface Lower extends Upper {}
class Foo implements Lower { boolean isBar() { true } }
class Bar extends Foo {}
boolean foo(Bar impl) {
impl.bar // isBar() called with the property notation
}
assert foo(new Bar())
'''
}
// GROOVY-5580: getName variant
void testGetNameFromSuperInterface() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
String foo(Lower impl) {
impl.getName()
}
assert foo({ 'bar' } as Lower) == 'bar'
'''
}
void testGetNameFromSuperInterfaceViaConcreteType1() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
class Foo implements Lower { String getName() { 'bar' } }
String foo(Foo impl) {
impl.getName()
}
assert foo(new Foo()) == 'bar'
'''
}
void testGetNameFromSuperInterfaceViaConcreteType2() {
assertScript '''
interface Upper { String getName() }
interface Lower extends Upper {}
class Foo implements Lower { String getName() { 'bar' } }
class Bar extends Foo {}
String foo(Bar impl) {
impl.getName()
}
assert foo(new Bar()) == 'bar'
'''
}
void testSpreadArgsRestrictedInNonStaticMethodCall() {
// GROOVY-10597
assertScript '''
def m(int i, String... strings) {
'' + i + strings.join('')
}
List<String> strings() {['3','4']}
assert m(1, '2', *strings(), '5') == '12345'
'''
shouldFailWithMessages '''
def foo(String one, String... zeroOrMore) {
}
def bar(String[] strings) {
foo(*strings)
}
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time'
shouldFailWithMessages '''
def foo(String a, String b, int c, double d, double e) {
}
def bar(String[] strings, int i, double[] numbers) {
foo(*strings, i, *numbers)
}
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'Cannot find matching method '
}
void testSpreadArgsRestrictedInStaticMethodCall() {
// GROOVY-10597
assertScript '''
static m(int i, String... strings) {
return '' + i + strings.join('')
}
List<String> strings = ['3','4']
assert m(1,'2',*strings,'5') == '12345'
'''
shouldFailWithMessages '''
static foo(String one, String... zeroOrMore) {
}
static bar(String[] strings) {
foo(*strings)
}
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time'
shouldFailWithMessages '''
static foo(String a, String b, int c, double d, double e) {
}
static bar(String[] strings, int i, double[] numbers) {
foo(*strings, i, *numbers)
}
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'Cannot find matching method '
}
void testSpreadArgsRestrictedInConstructorCall() {
// GROOVY-10597
assertScript '''
class C {
C(String one, String... zeroOrMore) {
String result = one + zeroOrMore.join('')
assert result == 'ABC'
}
}
new C('A', *['B'], 'C')
'''
shouldFailWithMessages '''
class C {
C(String one, String... zeroOrMore) {
}
}
new C(*['A','B'])
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time'
shouldFailWithMessages '''
class C {
C(String a, String b) {
}
}
new C(*['A','B'])
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'Cannot find matching method '
}
void testSpreadArgsRestrictedInClosureCall() {
// GROOVY-10597
assertScript '''
def closure = { String one, String... zeroOrMore ->
return one + zeroOrMore.join('')
}
String result = closure('A', *['B','C'])
assert result == 'ABC'
'''
shouldFailWithMessages '''
def closure = { String one, String... zeroOrMore -> }
def strings = ['A','B','C']
closure(*strings)
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time'
shouldFailWithMessages '''
def closure = { String a, String b, String c -> }
def strings = ['A','B','C']
closure(*strings)
''',
'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
'Cannot call closure that accepts [java.lang.String, java.lang.String, java.lang.String] with '
}
void testBoxingShouldCostMore() {
assertScript '''
int foo(int x) { 1 }
int foo(Integer x) { 2 }
@ASTTest(phase=INSTRUCTION_SELECTION, value={
lookup('mce').each {
def call = it.expression
def target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert target.parameters[0].type == int_TYPE
}
})
int bar() {
mce: foo(1)
}
bar()
// commented out the next line because this is something
// the dynamic runtime cannot ensure
//assert bar() == 1
'''
}
// GROOVY-5645
void testSuperCallWithVargs() {
assertScript '''
class Base {
int foo(int x, Object... args) { 1 }
int foo(Object... args) { 2 }
}
class Child extends Base {
void bar() {
assert foo(1, 'a') == 1
super.foo(1, 'a') == 1
}
}
new Child().bar()
'''
}
void testVargsSelection() {
assertScript '''
int foo(int x, Object... args) { 1 }
int foo(Object... args) { 2 }
assert foo(1) == 1
assert foo() == 2
assert foo(1,2) == 1
'''
}
// GROOVY-5702
void testShouldFindInterfaceMethod() {
assertScript '''
interface OtherCloseable {
void close()
}
abstract class MyCloseableChannel implements OtherCloseable { }
class Test {
static void test(MyCloseableChannel mc) {
mc?.close()
}
}
Test.test(null)
'''
}
void testShouldFindInheritedInterfaceMethod() {
assertScript '''
interface Top { void foo() }
interface Middle extends Top {}
interface Bottom extends Middle {}
void test(Bottom b) {
b.foo()
}
'''
}
void testShouldFindInheritedInterfaceMethod2() {
assertScript '''
interface Top { int foo(int i) }
interface Middle extends Top { int foo(String s) }
interface Bottom extends Middle {}
void test(Bottom b) {
b.foo(123)
}
'''
}
void testShouldFindInheritedInterfaceMethod3() {
assertScript '''
interface Top { int foo(int i) }
interface Middle extends Top { }
interface Bottom extends Middle { int foo(String s) }
void test(Bottom b) {
b.foo(123)
}
'''
}
void testShouldFindInheritedInterfaceMethod4() {
assertScript '''
interface Top { int foo(int i) }
interface Middle extends Top { int foo(String s) }
abstract class Bottom implements Middle {}
int test(Bottom b) {
b.foo(123)
}
def bot = new Bottom() {
int foo(int i) { 1 }
int foo(String s) { 2 }
}
assert test(bot) == 1
'''
}
void testShouldFindInheritedInterfaceMethod5() {
assertScript '''
interface Top { int foo(int i) }
interface Middle extends Top { }
abstract class Bottom implements Middle { abstract int foo(String s) }
int test(Bottom b) {
b.foo(123)
}
def bot = new Bottom() {
int foo(int i) { 1 }
int foo(String s) { 2 }
}
assert test(bot) == 1
'''
}
// GROOVY-9890
void testShouldFindInheritedInterfaceDefaultMethod() {
assertScript '''
class Impl implements groovy.bugs.groovy9890.Face {
@Override def foo(String s) {
return s
}
// abstract def foo(long n)
}
void test() {
def result = new Impl().foo(42L)
assert result.class == Long.class
}
test()
'''
}
// GROOVY-9890
void testShouldFindInheritedInterfaceDefaultMethodJava() {
assertScript '''
void test() {
def result = new groovy.bugs.groovy9890.ImplJ().foo(42L)
assert result.class == Long.class
}
test()
'''
}
// GROOVY-5743
void testClosureAsParameter() {
assertScript '''
Integer a( String s, Closure<Integer> b ) {
b( s )
}
assert a( 'tim' ) { 0 } == 0
'''
}
// GROOVY-5743
void testClosureAsParameterWithDefaultValue() {
assertScript '''
Integer a( String s, Closure<Integer> b = {String it -> it.length()}) {
b( s )
}
assert a( 'tim' ) == 3
'''
}
// GROOVY-5712
void testClassForNameVsCharsetForName() {
assertScript '''
import java.nio.charset.Charset
Charset charset = Charset.forName('UTF-8')
assert charset instanceof Charset
'''
}
// GROOVY-10341
void testCallAbstractSuperMethod() {
shouldFailWithMessages '''
abstract class Foo {
abstract def m()
}
class Bar extends Foo {
@Override
def m() {
super.m()
}
}
''',
'Abstract method m() cannot be called directly'
}
// GROOVY-5810
void testCallStaticSuperMethod() {
assertScript '''
class Top {
static boolean called = false
public static foo() {
called = true
}
}
class Bottom extends Top {
public static foo() {
super.foo()
}
}
Bottom.foo()
assert Top.called
'''
}
void testShouldFindSetProperty() {
assertScript '''
class A {
int x
void foo() {
this.setProperty('x', 1)
}
}
def a = new A()
a.foo()
assert a.x == 1
'''
}
// GROOVY-5888
void testStaticContextScoping() {
assertScript '''
class A {
static List foo = 'a,b,c'.split(/,/)*.trim()
}
assert A.foo == ['a','b','c']
'''
}
// GROOVY-6147
void testVargsCallWithOverloadedMethod() {
assertScript '''
int select(Object a, String s) { 1 }
int select(Object a, String s, Object[] args) { 2 }
def o = new Date()
def s = 'String'
@ASTTest(phase=INSTRUCTION_SELECTION,value={
def method = node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert method.name == 'select'
assert method.parameters.length==2
})
def result = select(o,s)
assert result == 1
'''
}
// GROOVY-6195
void testShouldNotThrowAmbiguousVargs() {
assertScript '''
def list = ['a', 'b', 'c']
Object[] arr = list.toArray()
println arr
'''
}
void testOverloadedMethodWithVargs() {
assertScript '''import org.codehaus.groovy.classgen.asm.sc.support.Groovy6235SupportSub as Support
def b = new Support()
assert b.overload() == 1
assert b.overload('a') == 1
assert b.overload('a','b') == 2
'''
}
// GROOVY-10720
void testOverloadedMethodWithArray() {
assertScript '''
Double[] array = new Double[1]
def stream = Arrays.stream(array) //stream(T[])
assert stream.map(d -> 'string')[0] == 'string'
'''
}
// GROOVY-5883, GROOVY-6270
void testClosureUpperBound() {
assertScript '''
class Test<T> {
def map(Closure<T> mapper) { 1 }
def m1(Closure<Boolean> predicate) {
map { T it -> return predicate(it) ? it : null }
}
def m2(Closure<Boolean> predicate) {
map { T it -> return predicate(it) ? it : (T) null }
}
def m3(Closure<Boolean> predicate) {
Closure<T> c = { T it -> return predicate(it) ? it : null }
map(c)
}
}
def t = new Test<String>()
assert t.m1{true} == 1
assert t.m2{true} == 1
assert t.m3{true} == 1
'''
}
// GROOVY-6569, GROOVY-6528
void testMoreExplicitErrorMessageOnStaticMethodNotFound() {
shouldFailWithMessages '''
Double.isFiniteMissing(2.0d)
''', 'Cannot find matching method java.lang.Double#isFiniteMissing(double)'
shouldFailWithMessages '''
String.doSomething()
''', 'Cannot find matching method java.lang.String#doSomething()'
}
// GROOVY-6646
void testNPlusVargsCallInOverloadSituation() {
assertScript '''
def foo(Class... cs) { "Classes" }
def foo(String... ss) { "Strings" }
assert foo(List, Map) == "Classes"
assert foo("2","1") == "Strings"
'''
assertScript '''
def foo(Class<?>... cs) { "Classes" }
def foo(String... ss) { "Strings" }
assert foo(List, Map) == "Classes"
assert foo("2","1") == "Strings"
'''
}
// GROOVY-6776
void testPrimtiveParameterAndNullArgument() {
shouldFailWithMessages '''
def foo(int i){}
def bar() {
foo null
}
bar()
''', '#foo(int) with arguments [<unknown parameter type>]'
}
// GROOVY-6751
void testMethodInBothInterfaceAndSuperclass() {
assertScript '''
interface Ifc {
Object getProperty(String s)
}
class DuplicateMethodInIfc implements Ifc {} // implemented in groovy.lang.GroovyObject
class Tester {
DuplicateMethodInIfc dup = new DuplicateMethodInIfc()
Object obj = dup.getProperty("foo")
}
try { new Tester()}
catch(groovy.lang.MissingPropertyException expected) {}
'''
}
// GROOVY-7813
void testNonStaticOuterMethodCannotBeCalledFromStaticClass() {
shouldFailWithMessages '''
class Foo {
def bar() { 2 }
static class Baz {
def doBar() { bar() }
}
}
null
''', 'Non-static method Foo#bar cannot be called from static context'
}
void testStaticOuterMethodCanBeCalledFromStaticClass() {
assertScript '''
class Foo {
static def bar() { 2 }
static class Baz {
def doBar() {
bar()
}
}
}
assert new Foo.Baz().doBar() == 2
'''
}
void testInheritedMethodCanBeCalledFromStaticClass() {
assertScript '''
class Bar {
def bar() { 1 }
}
class Foo {
static class Baz extends Bar {
def doBar() {
bar()
}
}
}
assert new Foo.Baz().doBar() == 1
'''
}
// GROOVY-8445
void testClosureToFunctionalInterface() {
assertScript '''
public class Main {
public static void main(String[] args) {
assert 13 == [1, 2, 3].stream().reduce(7, {Integer r, Integer e -> r + e})
}
}
'''
}
//--------------------------------------------------------------------------
static class MyMethodCallTestClass {
static String echo(String msg) {
msg
}
static int mul(int... ints) {
ints.toList().inject(1) { x,y -> x*y }
}
int add(int x, int y) { x+y }
int add(double x, double y) { 2*x+y }
int sum(int... args) { args.toList().sum() }
}
static class MyMethodCallTestClass2<T> extends MyMethodCallTestClass {
T[] identity(T... args) { args }
}
static class MyMethodCallTestClass3 extends MyMethodCallTestClass2<String> {}
static class GroovyPage {
final void printHtmlPart(int partNumber) {}
final void createTagBody(int bodyClosureIndex, Closure<?> bodyClosure) {}
}
static class BaseWithProtected {
protected int m() { 1 }
}
static class ChildWithPublic extends BaseWithProtected {
int m() { 2 }
}
static class Child2 extends ChildWithPublic {
}
}