Skip to content

Commit 0a0d2c8

Browse files
committed
GROOVY-11986: genericGetMethod registration too permissive: matches any get(X) where X is a supertype of String (test)
1 parent 65d16eb commit 0a0d2c8

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package bugs
20+
21+
import org.junit.jupiter.api.Test
22+
23+
import static groovy.test.GroovyAssert.assertScript
24+
25+
final class Groovy11986 {
26+
27+
// get(Serializable) is the GORM entity-by-ID lookup shape; it must not be
28+
// registered as the genericGetMethod, otherwise dynamic property access on
29+
// an instance hijacks propertyMissing(String).
30+
@Test
31+
void testInheritedGetSerializableDoesNotShadowPropertyMissing() {
32+
assertScript '''
33+
trait T {
34+
Object get(Serializable id) { "by-id:$id".toString() }
35+
Object propertyMissing(String name) { "missing:$name".toString() }
36+
}
37+
class C implements T {
38+
String title
39+
}
40+
41+
def c = new C(title: 'hello')
42+
assert c.title == 'hello' // bean property still wins
43+
assert c.foo == 'missing:foo' // dynamic access falls through to propertyMissing
44+
assert c.get('foo') == 'by-id:foo' // direct call still hits get(Serializable)
45+
'''
46+
}
47+
48+
@Test
49+
void testInheritedGetObjectDoesNotShadowPropertyMissing() {
50+
assertScript '''
51+
class Base {
52+
Object get(Object key) { "by-key:$key".toString() }
53+
}
54+
class C extends Base {
55+
Object propertyMissing(String name) { "missing:$name".toString() }
56+
}
57+
58+
def c = new C()
59+
assert c.foo == 'missing:foo'
60+
assert c.get('foo') == 'by-key:foo'
61+
'''
62+
}
63+
64+
// Sanity: a real get(String) is still picked up as the generic getter.
65+
@Test
66+
void testGetStringStillRegistersAsGenericGetter() {
67+
assertScript '''
68+
class C {
69+
def get(String name) { "value:$name".toString() }
70+
}
71+
72+
def c = new C()
73+
assert c.foo == 'value:foo'
74+
'''
75+
}
76+
77+
// Sanity: get(String) on a class that also inherits get(Serializable) wins
78+
// as the generic getter (closer parameter match).
79+
@Test
80+
void testGetStringWinsOverInheritedGetSerializable() {
81+
assertScript '''
82+
trait T {
83+
Object get(Serializable id) { "by-id:$id".toString() }
84+
}
85+
class C implements T {
86+
Object get(String name) { "by-name:$name".toString() }
87+
}
88+
89+
def c = new C()
90+
assert c.foo == 'by-name:foo'
91+
'''
92+
}
93+
}

0 commit comments

Comments
 (0)