Skip to content

Commit 1f1716d

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

File tree

5 files changed

+225
-31
lines changed

5 files changed

+225
-31
lines changed

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

+17-3
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

+16-2
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

+18-4
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

+159-10
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,8 +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]"
65-
assert root.exists() : 'Plugin DSLD classpath entry should exist'
64+
assert root != null && root.exists() : "Plugin DSLD classpath entry should exist. Exsting resolved roots: [\n${ -> elements.join(', ')}\n]\nOther DSLD fragments: [\n${ -> possibleFrags.join('\n')}\n]"
6665

6766
root.resource().refreshLocal(IResource.DEPTH_INFINITE, null)
6867
root.close(); root.open(null)
@@ -90,10 +89,57 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
9089
@Test
9190
void testDelegate2() {
9291
String contents = '''\
93-
|class Bar {
92+
|class Foo {
93+
| @Delegate List<Integer> list
94+
|}
95+
|new Foo().spliterator() // default method of List
96+
|'''.stripMargin()
97+
98+
inferType(contents, 'spliterator').with {
99+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
100+
assert declaringTypeName == 'java.util.List<java.lang.Integer>'
101+
assert typeName == 'java.util.Spliterator<java.lang.Integer>'
102+
}
103+
}
104+
105+
@Test
106+
void testDelegate3() {
107+
String contents = '''\
108+
|class Foo {
109+
| @Delegate List<Integer> list
110+
|}
111+
|new Foo().stream() // default method of Collection
112+
|'''.stripMargin()
113+
114+
inferType(contents, 'stream').with {
115+
assert result.extraDoc?.replace('}', '') =~ 'Delegate AST transform'
116+
assert declaringTypeName == 'java.util.List<java.lang.Integer>'
117+
assert typeName == 'java.util.stream.Stream<java.lang.Integer>'
118+
}
119+
}
120+
121+
@Test
122+
void testDelegate4() {
123+
String contents = '''\
124+
|class Foo {
125+
| @Delegate List<Integer> list
126+
|}
127+
|new Foo()./**/equals(null) // method of List and Object
128+
|'''.stripMargin()
129+
130+
inferType(contents, 'equals').with {
131+
assert declaringTypeName == 'java.lang.Object'
132+
assert result.extraDoc == null
133+
}
134+
}
135+
136+
@Test
137+
void testDelegate5() {
138+
String contents = '''\
139+
|class Foo {
94140
| @Delegate URL url
95141
|}
96-
|new Bar().file
142+
|new Foo().file // getFile() as property
97143
|'''.stripMargin()
98144

99145
inferType(contents, 'file').with {
@@ -104,7 +150,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
104150
}
105151

106152
@Test // GROOVY-5204
107-
void testDelegate3() {
153+
void testDelegate6() {
108154
String contents = '''\
109155
|class Bar {
110156
| def baz() {}
@@ -123,7 +169,7 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
123169
}
124170

125171
@Test // GROOVY-5204
126-
void testDelegate4() {
172+
void testDelegate7() {
127173
String contents = '''\
128174
|class Bar {
129175
| def baz() {}
@@ -143,14 +189,14 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
143189
}
144190

145191
@Test // GROOVY-3917
146-
void testDelegate5() {
192+
void testDelegate8() {
147193
String contents = '''\
148194
|class Bar {
149195
|}
150196
|class Foo {
151197
| @Delegate Bar bar = new Bar()
152198
|}
153-
|new Foo().getProperty('baz')
199+
|new Foo().getProperty('baz') // method of GroovyObject
154200
|'''.stripMargin()
155201

156202
inferType(contents, 'getProperty').with {
@@ -159,6 +205,109 @@ final class BuiltInDSLInferencingTests extends DSLInferencingTestSuite {
159205
}
160206
}
161207

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

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

0 commit comments

Comments
 (0)