blob: 0b92a41042e9f89aa3f820c9812f026fbb5e6f56 [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 groovy.transform.PackageScope
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
/**
* Unit tests for static type checking : fields and properties.
*/
class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
void testAssignFieldValue() {
assertScript '''
class C { int x }
C c = new C()
c.x = 1
'''
shouldFailWithMessages '''
class C { int x }
C c = new C()
c.x = '1'
''',
'Cannot assign value of type java.lang.String to variable of type int'
}
void testAssignFinalFieldValue() {
assertScript '''
class C {
final x = null
}
new C().x
'''
assertScript '''
class C {
final x
C() {
x = null
}
}
new C().x
'''
assertScript '''
class C {
final x
C(def x) {
this.x = x
}
}
new C(null).x
'''
assertScript '''
class C {
final x;
{
x = null
}
}
new C().x
'''
assertScript '''
class C {
final x;
{
this.x = x
}
}
new C().x
'''
assertScript '''
class C {
static final x
static {
this.x = null
}
}
new C().x
'''
}
void testAssignFinalFieldValue2() {
shouldFailWithMessages '''
int[] array = []
array.length = 1
''',
'Cannot set read-only property: length'
shouldFailWithMessages '''
class C { final x }
new C().x = null
''',
'Cannot set read-only property: x'
// GROOVY-5450
shouldFailWithMessages '''
class C { final x }
new C().@x = null
''',
'Cannot set read-only property: x'
shouldFailWithMessages '''
class C { final x }
new C().with { x = null }
''',
'Cannot set read-only property: x'
shouldFailWithMessages '''
class C { final x }
new C().with { delegate.x = null }
''',
'Cannot set read-only property: x'
shouldFailWithMessages '''
class C { final x }
new C().setX(null)
''',
'Cannot find matching method C#setX(<unknown parameter type>).'
}
void testInferenceFromFieldType() {
assertScript '''
class C {
String name = 'Cedric'
}
C c = new C()
def x = c.name
x.toUpperCase() // type of x should be inferred from field type
'''
}
void testAssignFieldValueWithAttributeNotation() {
assertScript '''
class C {
int x
}
C c = new C()
c.@x = 1
'''
}
void testAssignFieldValueWithWrongTypeAndAttributeNotation() {
shouldFailWithMessages '''
class C {
int x
}
C c = new C()
c.@x = '1'
''',
'Cannot assign value of type java.lang.String to variable of type int'
}
void testInferenceFromAttributeType() {
assertScript '''
class C {
String name = 'Cedric'
}
C c = new C()
def x = c.@name
x.toUpperCase() // type of x should be inferred from field type
'''
}
void testShouldComplainAboutMissingProperty() {
shouldFailWithMessages '''
Object o = new Object()
o.x = 0
''',
'No such property: x for class: java.lang.Object'
}
void testShouldComplainAboutMissingProperty2() {
shouldFailWithMessages '''
class C {
}
C c = new C()
c.x = 0
''',
'No such property: x for class: C'
}
// GROOVY-11319
void testShouldComplainAboutMissingProperty3() {
shouldFailWithMessages '''
class C {
private int getX() { 1 }
}
class D extends C {
void test() {
super.x
}
}
new D().test()
''',
'No such property: x for class: C'
}
// GROOVY-11319
void testShouldComplainAboutMissingProperty4() {
shouldFailWithMessages '''
class C {
private void setX(int i) {
assert false : 'cannot access'
}
}
class D extends C {
void test() {
super.x = 1
}
}
new D().test()
''',
'No such property: x for class: C'
}
void testShouldComplainAboutMissingProperty5() {
shouldFailWithMessages '''
class C {
private x
}
class D extends C {
void test() {
this.x
}
}
new D().test()
''',
'No such property: x for class: D'
}
void testShouldComplainAboutMissingAttribute() {
shouldFailWithMessages '''
Object o = new Object()
o.@x = 0
''',
'No such attribute: x for class: java.lang.Object'
}
void testShouldComplainAboutMissingAttribute2() {
shouldFailWithMessages '''
class C {
}
C c = new C()
c.@x = 0
''',
'No such attribute: x for class: C'
}
void testShouldComplainAboutMissingAttribute3() {
shouldFailWithMessages '''
class C {
def getX() { }
}
C c = new C()
println c.@x
''',
'No such attribute: x for class: C'
}
void testShouldComplainAboutMissingAttribute4() {
shouldFailWithMessages '''
class C {
def setX(x) { }
}
C c = new C()
c.@x = 0
''',
'No such attribute: x for class: C'
}
void testShouldComplainAboutMissingAttribute5() {
shouldFailWithMessages '''
class C {
private x
}
class D extends C {
void test() {
this.@x
}
}
new D().test()
''',
'Cannot access field: x of class: C'
}
void testPropertyWithInheritance() {
assertScript '''
class C {
int x
}
class D extends C {
}
D d = new D()
assert d.x == 0
d.x = 2
assert d.x == 2
'''
}
void testPropertyTypeWithInheritance() {
shouldFailWithMessages '''
class C {
int x
}
class D extends C {
}
D d = new D()
d.x = '2'
''',
'Cannot assign value of type java.lang.String to variable of type int'
}
void testPropertyWithInheritanceFromAnotherSourceUnit() {
assertScript '''
class C extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass {
}
C c = new C()
c.x = 2
'''
}
void testPropertyWithInheritanceFromAnotherSourceUnit2() {
shouldFailWithMessages '''
class C extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass {
}
C c = new C()
c.x = '2'
''',
'Cannot assign value of type java.lang.String to variable of type int'
}
void testPropertyWithSuperInheritanceFromAnotherSourceUnit() {
assertScript '''
class C extends groovy.transform.stc.FieldsAndPropertiesSTCTest.BaseClass2 {
}
C c = new C()
c.x = 2
'''
}
// GROOVY-9955
void testStaticPropertyWithInheritanceFromAnotherSourceUnit() {
assertScript """import ${Public.canonicalName}
assert Public.answer == 42
assert Public.CONST == 'XX'
assert Public.VALUE == null
Public.VALUE = 'YY'
assert Public.VALUE == 'YY'
Public.@VALUE = 'ZZ'
assert Public.@VALUE == 'ZZ'
Public.VALUE = null
"""
}
// GROOVY-10695
void testStaticPropertyOfSelfType() {
for (qual in ['', 'this.', 'C.']) {
assertScript """
class C {
private static Object value
static Object getValue() {
${qual}value
}
static void setValue(v) {
${qual}value = v
}
}
C.setValue(null) // StackOverflowError
"""
}
}
void testDateProperties() {
assertScript '''
Date d = new Date()
def time = d.time
d.time = 0
'''
}
void testGetterForProperty1() {
assertScript '''
class C {
String p
}
def x = new C().getP()
x = x?.toUpperCase()
'''
}
// GROOVY-10981
void testGetterForProperty2() {
for (mode in ['', 'public', 'private', 'protected', '@groovy.transform.PackageScope']) {
assertScript """
abstract class A {
$mode Object p = 'field'
CharSequence getP() { 'property' }
}
class C extends A {
def m() {
final int len = p.length()
if (p instanceof String) {
p.toLowerCase()
p.toUpperCase()
}
}
}
String which = new C().m()
assert which == 'PROPERTY'
"""
}
}
// GROOVY-9973
void testGetterForProperty3() {
assertScript '''
class C {
private int f
int getP() { f }
Integer m() { 123456 - p }
Integer m(int i) { i - p }
}
def c = new C()
assert c.m() == 123456 // BUG! exception in phase 'class generation' ...
assert c.m(123) == 123 // ClassCastException: class org.codehaus.groovy.ast.Parameter cannot be cast to ...
'''
}
// GROOVY-11005
void testGetterForProperty4() {
File parentDir = File.createTempDir()
config.with {
targetDirectory = File.createTempDir()
jointCompilationOptions = [memStub: true]
}
try {
def a = new File(parentDir, 'Pogo.groovy')
a.write '''
class Pogo {
String value
String getValue() { value }
}
'''
def b = new File(parentDir, 'Test.groovy')
b.write '''
class Test extends Pogo {
void test() {
value = 'string'
}
}
'''
def loader = new GroovyClassLoader(this.class.classLoader)
def cu = new JavaAwareCompilationUnit(config, loader)
cu.addSources(a, b)
cu.compile()
loader.loadClass('Test').newInstance().test()
} finally {
parentDir.deleteDir()
config.targetDirectory.deleteDir()
}
}
// GROOVY-5232
void testSetterForProperty1() {
assertScript '''
class Person {
String name
static Person create() {
def p = new Person()
p.setName("Guillaume")
// but p.name = "Guillaume" works
return p
}
}
Person.create()
'''
}
// GROOVY-11372
void testSetterForProperty2() {
assertScript '''
def baos = new ByteArrayOutputStream()
assert baos.size() == 0
baos.bytes= new byte[1]
assert baos.size() == 1
'''
}
// GROOVY-5443
void testFieldInitShouldPass() {
assertScript '''
class C {
int bar = 1
}
new C()
'''
}
// GROOVY-5443
void testFieldInitShouldNotPassBecauseOfIncompatibleTypes() {
shouldFailWithMessages '''
class C {
int bar = new Date()
}
new C()
''',
'Cannot assign value of type java.util.Date to variable of type int'
}
// GROOVY-5443, GROOVY-10277
void testFieldInitShouldNotPassBecauseOfIncompatibleTypesWithClosure() {
shouldFailWithMessages '''
class C {
Closure<List> bar = { Date date -> date.getTime() }
}
''',
'Cannot return value of type long for closure expecting java.util.List'
shouldFailWithMessages '''
class C {
java.util.function.Supplier<String> bar = { -> 123 }
}
''',
'Cannot return value of type int for closure expecting java.lang.String'
}
// GROOVY-9882
void testFieldInitShouldPassForCompatibleTypesWithClosure() {
assertScript '''
class C {
java.util.function.Supplier<String> bar = { 'abc' }
}
assert new C().bar.get() == 'abc'
'''
}
void testClosureParameterMismatch() {
shouldFailWithMessages '''
class C {
java.util.function.Supplier<String> bar = { baz -> '' }
}
''',
'Wrong number of parameters for method target: get()'
shouldFailWithMessages '''
class C {
java.util.function.Consumer<String> bar = { -> null }
}
''',
'Wrong number of parameters for method target: accept(java.lang.String)'
}
// GROOVY-5585
void testClassPropertyOnInterface() {
assertScript '''
Class test(Serializable arg) {
Class<?> clazz = arg.class
clazz
}
assert test('foo') == String
'''
assertScript '''
Class test(Serializable arg) {
Class<?> clazz = arg.getClass()
clazz
}
assert test('foo') == String
'''
}
void testSetterUsingPropertyNotation() {
assertScript '''
class C {
boolean ok = false
void setFoo(String foo) { ok = (foo == 'foo') }
}
def c = new C()
c.foo = 'foo'
assert c.ok
'''
}
void testSetterUsingPropertyNotationOnInterface() {
assertScript '''
interface FooAware { void setFoo(String arg) }
class C implements FooAware {
void setFoo(String foo) { }
}
void test(FooAware fa) {
fa.foo = 'foo'
}
def c = new C()
test(c)
'''
}
void testListDotProperty1() {
assertScript '''class Elem { int value }
List<Elem> list = new LinkedList<Elem>()
list.add(new Elem(value:123))
list.add(new Elem(value:456))
assert list.value == [ 123, 456 ]
list.add(new Elem(value:789))
assert list.value == [ 123, 456, 789 ]
'''
assertScript '''class Elem { String value }
List<Elem> list = new LinkedList<Elem>()
list.add(new Elem(value:'123'))
list.add(new Elem(value:'456'))
assert list.value == [ '123', '456' ]
list.add(new Elem(value:'789'))
assert list.value == [ '123', '456', '789' ]
'''
}
void testListDotProperty2() {
assertScript '''
class C { int x }
def list = [new C(x:1), new C(x:2)]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
def type = node.getNodeMetaData(INFERRED_TYPE)
assert type.toString(false) == 'java.util.List<java.lang.Integer>'
})
def x = list.x
assert x == [1,2]
'''
}
// GROOVY-5700
void testMapPropertyAccess1() {
assertScript '''
def map = [key: 123]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
})
def val = map.key
assert val == 123
'''
}
// GROOVY-8788
void testMapPropertyAccess2() {
assertScript '''
def map = [key: 123]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
})
def val = map['key']
assert val == 123
'''
}
// GROOVY-5988
void testMapPropertyAccess3() {
assertScript '''
String key = 'name'
Map<String, Integer> map = [:]
map[key] = 123
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
})
def val = map[key]
assert val == 123
'''
}
// GROOVY-5797
void testMapPropertyAccess4() {
assertScript '''
def test(Map foo) {
def map = [baz: 1]
map[ foo.bar ]
}
assert test(bar:'baz') == 1
'''
}
// GROOVY-11369
void testMapPropertyAccess5() {
assertScript '''
def map = [:]
assert map.entry == null
assert map.empty == null
assert map.class == null
assert map.metaClass == null // TODO
assert map.properties != null
map.entry = null
map.empty = null // not read-only property
map.class = null // not read-only property
map.metaClass = null // not read-only property
map.properties = null
assert map.containsKey('entry')
assert map.containsKey('empty')
assert map.containsKey('class')
assert !map.containsKey('metaClass')
assert map.containsKey('properties')
'''
}
// GROOVY-8074
void testMapPropertyAccess6() {
assertScript '''
class C extends HashMap {
def foo = 1
}
def map = new C()
map.put('foo', 11)
assert map.foo == 1
assert map['foo'] == 1
'''
assertScript """
def map = new ${MapType.name}()
map.put('foo', 11)
assert map.foo == 1
assert map['foo'] == 1
map.put('bar', 22)
assert map.bar == 22
assert map['bar'] == 22
map.put('baz', 33)
assert map.baz == 33
assert map['baz'] == 33
"""
}
// GROOVY-5001, GROOVY-5491, GROOVY-6144
void testMapPropertyAccess7() {
String types = '''
class A { }
class B { }
class C extends HashMap<String,A> {
B b = new B()
}
'''
assertScript types + '''
def map = new C()
map.put('a', new A())
assert map.get('a') != null
assert map.get('b') == null
A a = map.a
B b = map.b
a = map['a']
b = map['b']
assert a instanceof A
assert b instanceof B
'''
assertScript types + '''
def test(C map) {
A a = map.a
B b = map.b
a = map['a']
b = map['b']
assert a instanceof A
assert b instanceof B
}
test(new C().tap{ put('a', new A()) })
'''
}
// GROOVY-5517
void testMapPropertyAccess8() {
String type = '''
@groovy.transform.stc.POJO
@groovy.transform.CompileStatic
class C extends HashMap {
public static int version = 666
}
'''
assertScript type + '''
def map = new C()
map.foo = 123
def value = map.foo
assert value == 123
map['foo'] = 4.5
value = map['foo']
assert value == 4.5
value = map.version
assert value == 666
'''
assertScript type + '''
def test(C map) {
map.foo = 123
def value = map.foo
assert value == 123
map['foo'] = 4.5
value = map['foo']
assert value == 4.5
value = map.version
assert value == 666
}
test(new C())
'''
}
// GROOVY-11368
void testMapPropertyAccess9() {
String type = '''
class C implements Map<String,String> {
@Delegate Map<String,String> impl = [:]
}
'''
assertScript type + '''
def map = new C()
assert map.entry == null
assert map.empty == null
assert map.class == null
assert map.metaClass == null
'''
assertScript type + '''
def test(C map) { // no diff
assert map.entry == null
assert map.empty == null
assert map.class == null
assert map.metaClass == null
}
test(new C())
'''
}
// GROOVY-11223
void testMapPropertyAccess10() {
assertScript """
def map = new ${MapType.name}()
map.foo = 11 // public setter
assert map.foo == 11
assert map.getFoo() == 11
assert map.get('foo') == null
"""
assertScript """
def map = new ${MapType.name}()
map.bar = 22 // protected setter
assert map.bar == null
assert map.@bar == 2
"""
assertScript """
def map = new ${MapType.name}()
map.baz = 33 // package-private setter
assert map.baz == null
assert map.@baz == 3
"""
}
void testTypeCheckerDoesNotThinkPropertyIsReadOnly() {
assertScript '''
// a base class defining a read-only property
class C {
private String foo = 'foo'
String getFoo() { foo }
String getFooFromC() { foo }
}
// a subclass defining its own field
class D extends C {
D(String msg) {
this.foo = msg
}
private String foo
public String getFoo() { this.foo }
}
def d = new D('bar')
assert d.foo == 'bar'
assert d.fooFromC == 'foo'
'''
}
// GROOVY-5779
void testShouldNotUseNonStaticProperty() {
assertScript '''import java.awt.Color
Color c = Color.red // should not be interpreted as Color.getRed()
'''
}
// GROOVY-5725
void testAccessFieldDefinedInInterface() {
assertScript '''
class C implements groovy.transform.stc.FieldsAndPropertiesSTCTest.InterfaceWithField {
void test() {
assert boo == "I don't fancy fields in interfaces"
}
}
new C().test()
'''
}
void testOuterPropertyAccess1() {
assertScript '''
class Outer {
class Inner {
def m() {
p
}
}
def p = 1
}
def i = new Outer.Inner(new Outer())
def x = i.m()
assert x == 1
'''
}
void testOuterPropertyAccess2() {
assertScript '''
class Outer {
class Inner {
def m() {
getP()
}
}
def p = 1
}
def i = new Outer.Inner(new Outer())
def x = i.m()
assert x == 1
'''
}
// GROOVY-10414
void testOuterPropertyAccess3() {
assertScript '''
class Outer {
class Inner {
def m() {
setP(2)
getP()
}
}
def p = 1
}
def i = new Outer.Inner(new Outer())
def x = i.m()
assert x == 2
'''
}
// GROOVY-8050
void testOuterPropertyAccess4() {
shouldFailWithMessages '''
class Outer {
class Inner {
}
def p = 1
}
def i = new Outer.Inner(new Outer())
def x = i.p
''',
'No such property: p for class: Outer$Inner'
}
// GROOVY-8050
void testOuterPropertyAccess5() {
shouldFailWithMessages '''
class Outer {
class Inner {
}
def p = 1
}
def i = new Outer.Inner(new Outer())
def x = i.getP()
''',
'Cannot find matching method Outer$Inner#getP()'
}
// GROOVY-9598
void testOuterPropertyAccess6() {
shouldFailWithMessages '''
class Outer {
static class Inner {
def m() {
p
}
}
def p = 1
}
''',
'The variable [p] is undeclared.'
}
void testOuterPropertyAccess7() {
shouldFailWithMessages '''
class Outer {
static class Inner {
def m() {
this.p
}
}
def p = 1
}
''',
'No such property: p for class: Outer$Inner'
}
// GROOVY-7024
void testOuterPropertyAccess8() {
assertScript '''
class Outer {
static Map props = [bar: 10, baz: 20]
enum Inner {
FOO('foo');
Inner(String name) {
props[name] = 30
}
}
}
def foo = Outer.Inner.FOO
assert Outer.props == [bar: 10, baz: 20, foo: 30]
'''
}
void testOuterPropertyAccess9() {
assertScript '''
class Outer {
static final int ONE = 1
enum Inner {
CONST(1 + ONE)
final int value
Inner(int value) {
this.value = value
}
}
}
assert Outer.Inner.CONST.value == 2
'''
}
void testOuterPropertyAccess10() {
assertScript '''
class Outer {
class Inner {
def m() { p }
}
String p = 'field'
String getP() { 'property' }
}
String which = new Outer.Inner(new Outer()).m()
assert which == 'property'
'''
}
// GROOVY-11199
void testOuterPropertyAccess11() {
assertScript '''
class Outer {
class Inner {
def m() { p = 'method' }
}
String p = 'field'
String getP() { 'property' }
}
String which = new Outer.Inner(new Outer()).m()
assert which == 'method'
'''
}
// GROOVY-10981, GROOVY-10985
void testOuterPropertyAccess12() {
for (propertySource in [
'def get(String name){if(name=="VALUE")return 2}',
'def getProperty(String name){if(name=="VALUE")return 2}',
'@Delegate private Map<String,Object> map = [VALUE:2]']) {
assertScript """
class Outer {
static private int VALUE = 1
static class Inner {
$propertySource
def test(int i) {
if (i > VALUE) {
// ...
}
return VALUE
}
}
}
Object value = new Outer.Inner().test(0)
assert value == 2
"""
}
}
// GROOVY-11029
void testSuperPropertyAccess1() {
assertScript '''
class C {
Object thing
}
class D extends C {
@Override
Object getThing() {
super.thing
}
@Override
void setThing(object) {
super.thing = object
}
}
def d = new D()
d.thing = 'value'
assert d.thing == 'value'
'''
}
void testSuperPropertyAccess2() {
assertScript '''
abstract class A implements java.util.function.IntSupplier {
final int prop = 1
}
class C {
final int prop = 2
A m() {
new A() {
int getAsInt() {
prop
}
}
}
}
Number which = new C().m().getAsInt()
assert which == 1 // super before outer
'''
}
// GROOVY-9562
void testSuperPropertyAccess3() {
assertScript '''
abstract class A {
final int prop = 1
}
abstract class B {
final int prop = 2
abstract int baz()
}
class C extends A {
B bar() {
new B() {
int baz() {
prop
}
}
}
}
Number which = new C().bar().baz()
assert which == 2 // super before outer
'''
}
void testPrivateFieldAccessInClosure1() {
assertScript '''
class C {
private int x
void test() {
def func = { -> x = 666 }
func()
assert x == 666
}
}
new C().test()
'''
}
// GROOVY-9683
void testPrivateFieldAccessInClosure2() {
assertScript '''
class C {
private static X = 'xxx'
void test() {
[:].withDefault { throw new MissingPropertyException(it.toString()) }.with {
assert X == 'xxx'
}
}
}
new C().test()
'''
}
// GROOVY-11198
void testPrivateFieldAccessInEnumInit() {
for (mode in ['public', 'private', 'protected', '@groovy.transform.PackageScope']) {
assertScript """
class C {
$mode static int ONE = 1
enum E {
FOO(1 + ONE)
final number
E(int number) {
this.number = number
}
}
}
assert C.E.FOO.number == 2
"""
}
}
// GROOVY-11358
void testPrivateFieldAccessOfAbstract() {
shouldFailWithMessages '''
abstract class A {
private int f
}
void test(A a) {
int i = a.@f // MissingFieldException
int j = a.f // MissingPropertyException
}
test(new A() {})
''',
'Cannot access field: f of class: A', 'No such property: f for class: A'
shouldFailWithMessages '''
abstract class A {
private int f
}
void test(A a) {
a.@f = 1 // MissingFieldException
a.f = 2 // MissingPropertyException
}
test(new A() {})
''',
'Cannot access field: f of class: A', 'No such property: f for class: A'
}
// GROOVY-5737
void testGeneratedFieldAccessInClosure() {
assertScript '''
@groovy.util.logging.Log
class GreetingActor {
def receive = {
log.info "test"
}
}
new GreetingActor()
'''
}
// GROOVY-6277
void testPublicFieldVersusPrivateGetter() {
assertScript '''
class C {
private String getWho() { 'C' }
}
class D extends C {
public String who = 'D'
}
String result = new D().who
assert result == 'D'
'''
}
void testProtectedAccessorFromSamePackage() {
assertScript '''
class C {
protected String getWho() { 'C' }
}
class D {
def m(C c) {
def x = c.who
x.toLowerCase()
}
}
String result = new D().m(new C())
assert result == 'c'
'''
}
// GROOVY-6610
void testPrivateStaticFieldAccessBeforeThis() {
assertScript '''
class Outer {
static class Inner {
public final String value
Inner(String string) {
value = string
}
Inner() {
this(VALUE.toString())
}
}
private static Integer VALUE = 42
static main(args) {
assert new Inner().value == '42'
}
}
'''
}
// GROOVY-7890
void testNonStaticPropertyAndStaticMethodClosure() {
shouldFailWithMessages '''
class C {
List<String> replace
static String m(String s) {
s.collectReplacements {
(it in replace) ? 'o' : null
}
}
}
''',
'The variable [replace] is undeclared'
assertScript '''
class C {
List<String> replace
String m(String s) {
s.collectReplacements {
(it in replace) ? 'o' : null
}
}
}
String result = new C(replace:['a','b','c']).m('foobar')
assert result == 'foooor'
'''
}
// GROOVY-5872
void testAssignNullToFieldWithGenericsShouldNotThrowError() {
assertScript '''
class C {
List<String> list = null // should not throw an error
}
new C()
'''
}
void testSetterInWith() {
assertScript '''
class C {
private int y
void setFoo(int x) { y = x }
int value() { y }
}
def c = new C()
c.with {
setFoo(5)
}
assert c.value() == 5
'''
}
void testSetterInWithUsingPropertyNotation() {
assertScript '''
class C {
private int y
void setFoo(int x) { y = x }
int value() { y }
}
def c = new C()
c.with {
foo = 5
}
assert c.value() == 5
'''
}
void testSetterInWithUsingPropertyNotationAndClosureSharedVariable() {
assertScript '''
class C {
private int y
void setFoo(int x) { y = x }
int value() { y }
}
def c = new C()
def csv = 0
c.with {
foo = 5
csv = 10
}
assert c.value() == 5
assert csv == 10
'''
}
// GROOVY-9653
void testSetterInWithUsingPropertyNotation_DelegateAndOwnerHaveSetter() {
assertScript '''
class C {
final result = new D().with {
something = 'value' // ClassCastException: D cannot be cast to C
return object
}
void setSomething(value) { }
}
class D {
void setSomething(value) { }
Object getObject() { 'works' }
}
assert new C().result == 'works'
'''
}
// GROOVY-6230
void testAttributeWithGetterOfDifferentType() {
assertScript '''import java.awt.Dimension
def d = new Dimension(800,600)
@ASTTest(phase=INSTRUCTION_SELECTION,value={
def rit = node.rightExpression.getNodeMetaData(INFERRED_TYPE)
assert rit == int_TYPE
})
int width = d.@width
assert width == 800
assert (d.@width).getClass() == Integer
'''
}
// GROOVY-6489
void testShouldNotThrowUnmatchedGenericsError() {
assertScript '''
public class Foo {
private List<String> names;
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
}
class FooWorker {
public void doSomething() {
new Foo().with {
names = new ArrayList()
}
}
}
new FooWorker().doSomething()
'''
}
void testShouldFailWithIncompatibleGenericTypes() {
shouldFailWithMessages '''
public class Foo {
private List<String> names;
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
}
class FooWorker {
public void doSomething() {
new Foo().with {
names = new ArrayList<Integer>()
}
}
}
new FooWorker().doSomething()
''',
'Incompatible generic argument types. Cannot assign java.util.ArrayList<java.lang.Integer> to: java.util.List<java.lang.String>'
}
void testAICAsStaticProperty() {
assertScript '''
class Foo {
static x = new Object() {}
}
assert Foo.x instanceof Object
'''
}
void testPropertyWithMultipleSetters() {
assertScript '''
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.ast.stmt.*
class C {
private field
void setX(Integer a) {field=a}
void setX(String b) {field=b}
def getX(){field}
}
@ASTTest(phase=INSTRUCTION_SELECTION, value={
lookup('test1').each { stmt ->
def exp = stmt.expression
assert exp instanceof BinaryExpression
def left = exp.leftExpression
def md = left.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert md
assert md.name == 'setX'
assert md.parameters[0].originType == Integer_TYPE
}
lookup('test2').each { stmt ->
def exp = stmt.expression
assert exp instanceof BinaryExpression
def left = exp.leftExpression
def md = left.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
assert md
assert md.name == 'setX'
assert md.parameters[0].originType == STRING_TYPE
}
})
void test() {
def c = new C()
test1:
c.x = 1
assert c.x==1
test2:
c.x = "3"
assert c.x == "3"
}
test()
'''
}
// GROOVY-9893
void testPropertyWithMultipleSetters2() {
assertScript '''
abstract class A { String which
void setX(String s) { which = 'String' }
}
class C extends A {
void setX(boolean b) { which = 'boolean' }
}
def c = new C()
c.x = 'value'
assert c.which == 'String'
'''
}
// GROOVY-9893
void testPropertyWithMultipleSetters3() {
assertScript '''
interface I {
void setX(String s)
}
abstract class A implements I { String which
void setX(boolean b) { which = 'boolean' }
}
A a = new A() {
void setX(String s) { which = 'String' }
}
a.x = 'value'
assert a.which == 'String'
'''
}
// GROOVY-9893
void testPropertyWithMultipleSetters4() {
assertScript '''
trait T { String which
void setX(String s) { which = 'String' }
}
class C implements T {
void setX(boolean b) { which = 'boolean' }
}
def c = new C()
c.x = 'value'
assert c.which == 'String'
'''
}
void testPropertyAssignmentAsExpression() {
assertScript '''
class C {
int x = 2
}
def c = new C()
def x = c.x = 3
assert x == 3
'''
}
void testPropertyAssignmentInSubClassAndMultiSetter() {
10.times {
assertScript '''
class C {
int which
C() {
contentView = 42L
assert which == 2
}
void setContentView(Date value) { which = 1 }
void setContentView(Long value) { which = 2 }
}
class D extends C {
void m() {
contentView = 42L
assert which == 2
contentView = new Date()
assert which == 1
}
}
new D().m()
'''
}
}
void testPropertyAssignmentInSubClassAndMultiSetterThroughDelegation() {
10.times {
assertScript '''
class C {
int which
void setContentView(Date value) { which = 1 }
void setContentView(Long value) { which = 2 }
}
class D extends C {
}
new D().with {
contentView = 42L
assert which == 2
contentView = new Date()
assert which == 1
}
'''
}
}
void testShouldAcceptPropertyAssignmentEvenIfSetterOnlyBecauseOfSpecialType() {
assertScript '''
class BooleanSetterOnly {
void setFlag(boolean b) {}
}
def b = new BooleanSetterOnly()
b.flag = 'foo'
'''
assertScript '''
class StringSetterOnly {
void setFlag(String b) {}
}
def b = new StringSetterOnly()
b.flag = false
'''
assertScript '''
class ClassSetterOnly {
void setFlag(Class b) {}
}
def b = new ClassSetterOnly()
b.flag = 'java.lang.String'
'''
}
// GROOVY-6590
void testShouldFindStaticPropertyOnPrimitiveType() {
assertScript '''
int i=1
i.MAX_VALUE
'''
assertScript '''
def i="d"
i=1
i.MAX_VALUE
'''
}
// GROOVY-9855
void testShouldInlineStringConcatInTypeAnnotation() {
assertScript '''
@SuppressWarnings(C.PREFIX + 'checked') // not 'un'.plus('checked')
class C {
public static final String PREFIX = 'un'
}
new C()
'''
}
void testImplicitPropertyOfDelegateShouldNotPreferField() {
assertScript '''
Calendar.instance.with {
Date d1 = time // Date getTime() vs. long time
}
'''
}
void testPropertyStyleSetterArgShouldBeCheckedAgainstParamType() {
shouldFailWithMessages '''
class Foo {
Bar bar;
void setBar(int x) {
this.bar = new Bar(x: x)
}
}
class Bar {
int x
}
Foo foo = new Foo()
foo.bar = new Bar()
''',
'Cannot assign value of type Bar to variable of type int'
assertScript '''
class Foo {
Bar bar;
void setBar(int x) {
this.bar = new Bar(x: x)
}
}
class Bar {
int x
}
Foo foo = new Foo()
foo.bar = 1
assert foo.bar.x == 1
'''
}
void testPropertyStyleGetterUsageShouldBeCheckedAgainstReturnType() {
shouldFailWithMessages '''
class Foo {
Bar bar;
int getBar() {
bar.x
}
}
class Bar {
int x
}
Foo foo = new Foo(bar: new Bar(x: 1))
Bar bar = foo.bar
''',
'Cannot assign value of type int to variable of type Bar'
assertScript '''
class Foo {
Bar bar;
int getBar() {
bar.x
}
}
class Bar {
int x
}
Foo foo = new Foo(bar: new Bar(x: 1))
int x = foo.bar
assert x == 1
'''
}
//--------------------------------------------------------------------------
static interface InterfaceWithField {
String boo = "I don't fancy fields in interfaces"
}
static class MapType extends HashMap<String,Number> {
def foo = 1
protected bar = 2
@PackageScope baz = 3
protected void setBar(bar) {}
@PackageScope void setBaz(baz) {}
}
static class BaseClass {
int x
}
static class BaseClass2 extends BaseClass {
}
@PackageScope static class PackagePrivate {
public static Number getAnswer() { 42 }
public static final String CONST = 'XX'
public static String VALUE
}
static class Public extends PackagePrivate {
}
}