Skip to content

Commit d7251f0

Browse files
committed
Fix for #1528: DSLD: excludes, includes, deprecated, interfaces
1 parent e6ea331 commit d7251f0

File tree

5 files changed

+226
-31
lines changed

5 files changed

+226
-31
lines changed

base/org.codehaus.groovy30/plugin_dsld_support/dsld/basic_transforms.dsld

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.codehaus.groovy.ast.expr.*
2121

2222
import static org.apache.groovy.util.BeanUtils.capitalize
2323
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName
24+
import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringList
2425
import static org.codehaus.groovy.transform.NamedVariantASTTransformation.NAMED_PARAM_TYPE
2526

2627
@Field static final ClassNode NAMED_PARAMS_TYPE = ClassHelper.makeWithoutCaching(NamedParams)
@@ -45,8 +46,21 @@ contribute(currentType(annos: annotatedBy(Category))) {
4546
//@groovy.lang.Delegate
4647
contribute(currentType(fields: fields(annotatedBy(Delegate)))) {
4748
provider = '{@link groovy.lang.Delegate Delegate} AST transform'
48-
for (FieldNode field : fields) {
49-
delegatesTo type: field.type
49+
for (FieldNode field : fields) { AnnotationNode node = field.getAnnotations(new ClassNode(Delegate))[0]
50+
List<String> excludes = getMemberStringList(node, 'excludes')
51+
List<String> includes = getMemberStringList(node, 'includes')
52+
if (!excludes && includes) {
53+
excludes = field.type.methods*.name - includes // inverse
54+
}
55+
56+
Boolean deprecated = node.getMember('deprecated')?.value
57+
Boolean interfaces = node.getMember('interfaces')?.value
58+
if (!deprecated && (!field.type.isInterface() || Boolean.FALSE.equals(interfaces))) {
59+
def names = field.type.methods.findAll(org.eclipse.jdt.groovy.core.util.GroovyUtils.&isDeprecated)*.name
60+
if (names) excludes = (excludes ? excludes + names : names) // TODO: What if name is deprecated and not?
61+
}
62+
63+
delegatesTo type: field.type, except: excludes
5064
}
5165
}
5266

@@ -106,7 +120,7 @@ contribute(enclosingClass(annos: annotatedBy(Newify)) | enclosingField(annos: an
106120
def resolveNewifyType = { String name ->
107121
if (name.split('\\.')[-1].matches(expr.text)) {
108122
ClassNode type = currentType.module.context.resolver.resolve(name)
109-
if (type != ClassHelper.DYNAMIC_TYPE) {
123+
if (!ClassHelper.isDynamicTyped(type)) {
110124
addNewifyMethods(type)
111125
}
112126
}

base/org.codehaus.groovy40/plugin_dsld_support/dsld/basic_transforms.dsld

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.codehaus.groovy.ast.expr.*
2121

2222
import static org.apache.groovy.util.BeanUtils.capitalize
2323
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName
24+
import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringList
2425
import static org.codehaus.groovy.transform.NamedVariantASTTransformation.NAMED_PARAM_TYPE
2526

2627
@Field static final ClassNode NAMED_PARAMS_TYPE = ClassHelper.makeWithoutCaching(NamedParams)
@@ -45,8 +46,21 @@ contribute(currentType(annos: annotatedBy(Category))) {
4546
//@groovy.lang.Delegate
4647
contribute(currentType(fields: fields(annotatedBy(Delegate)))) {
4748
provider = '{@link groovy.lang.Delegate Delegate} AST transform'
48-
for (FieldNode field : fields) {
49-
delegatesTo type: field.type
49+
for (FieldNode field : fields) { AnnotationNode node = field.getAnnotations(new ClassNode(Delegate))[0]
50+
List<String> excludes = getMemberStringList(node, 'excludes')
51+
List<String> includes = getMemberStringList(node, 'includes')
52+
if (!excludes && includes) {
53+
excludes = field.type.methods*.name - includes // inverse
54+
}
55+
56+
Boolean deprecated = node.getMember('deprecated')?.value
57+
Boolean interfaces = node.getMember('interfaces')?.value
58+
if (!deprecated && (!field.type.isInterface() || Boolean.FALSE.equals(interfaces))) {
59+
def names = field.type.methods.findAll(org.eclipse.jdt.groovy.core.util.GroovyUtils.&isDeprecated)*.name
60+
if (names) excludes = (excludes ? excludes + names : names) // TODO: What if name is deprecated and not?
61+
}
62+
63+
delegatesTo type: field.type, except: excludes
5064
}
5165
}
5266

base/org.codehaus.groovy50/plugin_dsld_support/dsld/basic_transforms.dsld

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.codehaus.groovy.ast.expr.*
2121

2222
import static org.apache.groovy.util.BeanUtils.capitalize
2323
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName
24+
import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringList
2425

2526
// http://groovy-lang.org/metaprogramming.html#_available_ast_transformations
2627

@@ -42,8 +43,21 @@ contribute(currentType(annos: annotatedBy(Category))) {
4243
//@groovy.lang.Delegate
4344
contribute(currentType(fields: fields(annotatedBy(Delegate)))) {
4445
provider = '{@link groovy.lang.Delegate Delegate} AST transform'
45-
for (FieldNode field : fields) {
46-
delegatesTo type: field.type
46+
for (FieldNode field : fields) { AnnotationNode node = field.getAnnotations(new ClassNode(Delegate))[0]
47+
List<String> excludes = getMemberStringList(node, 'excludes')
48+
List<String> includes = getMemberStringList(node, 'includes')
49+
if (!excludes && includes) {
50+
excludes = field.type.methods*.name - includes // inverse
51+
}
52+
53+
Boolean deprecated = node.getMember('deprecated')?.value
54+
Boolean interfaces = node.getMember('interfaces')?.value
55+
if (!deprecated && (!field.type.isInterface() || Boolean.FALSE.equals(interfaces))) {
56+
def names = field.type.methods.findAll(org.eclipse.jdt.groovy.core.util.GroovyUtils.&isDeprecated)*.name
57+
if (names) excludes = (excludes ? excludes + names : names) // TODO: What if name is deprecated and not?
58+
}
59+
60+
delegatesTo type: field.type, except: excludes
4761
}
4862
}
4963

@@ -218,8 +232,8 @@ contribute(currentType(meths: methods(hasArgument(annotatedBy(NamedParam))) | ha
218232
}
219233

220234
Map<String, ?> requiredParams = [:], optionalParams = [:]
221-
for (AnnotationNode anno : parm.getAnnotations(ClassHelper.make(NamedParam)) ?:
222-
parm.getAnnotations(ClassHelper.make(NamedParams)).collectMany { it.getMember('value').expressions*.value }) {
235+
for (AnnotationNode anno : parm.getAnnotations(new ClassNode(NamedParam)) ?:
236+
parm.getAnnotations(new ClassNode(NamedParams)).collectMany { it.getMember('value').expressions*.value }) {
223237
String name = anno.getMember('value').value
224238
def type = anno.getMember('type')?.type
225239
if (type == null) NamedParam.getMethod('type').defaultValue

ide-test/org.codehaus.groovy.eclipse.dsl.tests/src/org/codehaus/groovy/eclipse/dsl/tests/BuiltInDSLInferencingTests.groovy

Lines changed: 160 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
6161
}
6262

6363
assert pluginEntry != null : "Did not find the Plugin DSLD classpath entry. Exsting resolved roots: [\n${ -> elements.join(', ')}\n]\nOther DSLD fragments: [\n${ -> possibleFrags.join('\n')}\n]"
64-
assert root != null : "Plugin DSLD classpath entry should exist. Exsting resolved roots: [\n${ -> elements.join(', ')}\n]\nOther DSLD fragments: [\n${ -> possibleFrags.join('\n')}\n]"
64+
assert root != null : "Plugin DSLD classpath entry should exist. Exsting resolved roots: [\n${ -> elements.join(', ')}\n]\nOther DSLD fragments: [\n${ -> possibleFrags.join('\n')}\n]"
6565
assert root.exists() : 'Plugin DSLD classpath entry should exist'
6666

6767
root.resource().refreshLocal(IResource.DEPTH_INFINITE, null)
@@ -75,7 +75,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
7575
void testDelegate1() {
7676
String contents = '''\
7777
|class Foo {
78-
| @Delegate List<Integer> list
78+
| @Delegate() List<Integer> list
7979
|}
8080
|new Foo().get(0)
8181
|'''.stripMargin()
@@ -90,10 +90,57 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
9090
@Test
9191
void testDelegate2() {
9292
String contents = '''\
93-
|class Bar {
93+
|class Foo {
94+
| @Delegate List<Integer> list
95+
|}
96+
|new Foo().spliterator() // default method of List
97+
|'''.stripMargin()
98+
99+
inferType(contents, 'spliterator').with {
100+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
101+
assert declaringTypeName == 'java.util.List<java.lang.Integer>'
102+
assert typeName == 'java.util.Spliterator<java.lang.Integer>'
103+
}
104+
}
105+
106+
@Test
107+
void testDelegate3() {
108+
String contents = '''\
109+
|class Foo {
110+
| @Delegate List<Integer> list
111+
|}
112+
|new Foo().stream() // default method of Collection
113+
|'''.stripMargin()
114+
115+
inferType(contents, 'stream').with {
116+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
117+
assert declaringTypeName == 'java.util.List<java.lang.Integer>'
118+
assert typeName == 'java.util.stream.Stream<java.lang.Integer>'
119+
}
120+
}
121+
122+
@Test
123+
void testDelegate4() {
124+
String contents = '''\
125+
|class Foo {
126+
| @Delegate List<Integer> list
127+
|}
128+
|new Foo()./**/equals(null) // method of List and Object
129+
|'''.stripMargin()
130+
131+
inferType(contents, 'equals').with {
132+
assert declaringTypeName == 'java.lang.Object'
133+
assert result.extraDoc == null
134+
}
135+
}
136+
137+
@Test
138+
void testDelegate5() {
139+
String contents = '''\
140+
|class Foo {
94141
| @Delegate URL url
95142
|}
96-
|new Bar().file
143+
|new Foo().file // getFile() as property
97144
|'''.stripMargin()
98145

99146
inferType(contents, 'file').with {
@@ -104,7 +151,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
104151
}
105152

106153
@Test // GROOVY-5204
107-
void testDelegate3() {
154+
void testDelegate6() {
108155
String contents = '''\
109156
|class Bar {
110157
| def baz() {}
@@ -123,7 +170,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
123170
}
124171

125172
@Test // GROOVY-5204
126-
void testDelegate4() {
173+
void testDelegate7() {
127174
String contents = '''\
128175
|class Bar {
129176
| def baz() {}
@@ -143,14 +190,14 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
143190
}
144191

145192
@Test // GROOVY-3917
146-
void testDelegate5() {
193+
void testDelegate8() {
147194
String contents = '''\
148195
|class Bar {
149196
|}
150197
|class Foo {
151198
| @Delegate Bar bar = new Bar()
152199
|}
153-
|new Foo().getProperty('baz')
200+
|new Foo().getProperty('baz') // method of GroovyObject
154201
|'''.stripMargin()
155202

156203
inferType(contents, 'getProperty').with {
@@ -159,6 +206,109 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
159206
}
160207
}
161208

209+
@Test // GROOVY-8164
210+
void testDelegate9() {
211+
String contents = '''\
212+
|class Bar {
213+
| def baz
214+
|}
215+
|class Foo {
216+
| @Delegate Comparator<Bar> cmp
217+
|}
218+
|new Foo().comparing(Bar.&getBaz) // static method of Comparator
219+
|'''.stripMargin()
220+
221+
inferType(contents, 'comparing').with {
222+
assert result.confidence.name() == 'UNKNOWN'
223+
}
224+
}
225+
226+
@Test
227+
void testDelegate10() {
228+
String contents = '''\
229+
|class Bar {
230+
| def baz
231+
|}
232+
|class Foo {
233+
| @Delegate(excludes=['compare','equals']) Comparator<Bar> cmp
234+
|}
235+
|new Foo().compare(null, null)
236+
|'''.stripMargin()
237+
238+
inferType(contents, 'compare').with {
239+
assert result.confidence.name() == 'UNKNOWN'
240+
}
241+
}
242+
243+
@Test
244+
void testDelegate11() {
245+
String contents = '''\
246+
|class Bar {
247+
| def baz
248+
|}
249+
|class Foo {
250+
| @Delegate(includes='compare') Comparator<Bar> cmp
251+
|}
252+
|new Foo().compare(null, null)
253+
|'''.stripMargin()
254+
255+
inferType(contents, 'compare').with {
256+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
257+
assert declaringTypeName == 'java.util.Comparator<Bar>'
258+
assert typeName == 'java.lang.Integer'
259+
}
260+
}
261+
262+
@Test
263+
void testDelegate12() {
264+
addGroovySource '''\
265+
|class Bar {
266+
| @Deprecated
267+
| int baz(){}
268+
|}
269+
|'''.stripMargin(), 'Bar'
270+
271+
String contents = '''\
272+
|class Foo {
273+
| @Delegate Bar bar
274+
|}
275+
|new Foo().baz()
276+
|'''.stripMargin()
277+
278+
inferType(contents, 'baz').with {
279+
assert result.confidence.name() == 'UNKNOWN'
280+
}
281+
282+
contents = contents.replace('@Delegate', '@Delegate(deprecated=true)')
283+
284+
inferType(contents, 'baz').with {
285+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
286+
assert declaringTypeName == 'Bar'
287+
assert typeName == 'java.lang.Integer'
288+
}
289+
}
290+
291+
@Test
292+
void testDelegate13() {
293+
addGroovySource '''\
294+
|interface Bar {
295+
| @Deprecated
296+
| int baz()
297+
|}
298+
|'''.stripMargin(), 'Bar'
299+
300+
String contents = '''\
301+
|class Foo {
302+
| @Delegate(interfaces=false) Bar bar
303+
|}
304+
|new Foo().baz()
305+
|'''.stripMargin()
306+
307+
inferType(contents, 'baz').with {
308+
assert result.confidence.name() == 'UNKNOWN'
309+
}
310+
}
311+
162312
@Test
163313
void testField1() {
164314
String contents = '''\
@@ -382,7 +532,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
382532
|class E {
383533
| String value
384534
|}
385-
|new E().compareTo(null)
535+
|new E()./**/compareTo(null)
386536
|'''.stripMargin()
387537

388538
inferType(contents, 'compareTo').with {

0 commit comments

Comments
 (0)