Skip to content

Commit 940303c

Browse files
committed
GROOVY-10075
1 parent 2ef6ab7 commit 940303c

File tree

7 files changed

+164
-26
lines changed

7 files changed

+164
-26
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/builder/FullProjectTests.java

+67
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
import org.codehaus.groovy.ast.ClassNode;
2121
import org.codehaus.groovy.ast.MethodNode;
2222
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
23+
import org.eclipse.core.resources.IMarker;
2324
import org.eclipse.core.runtime.IPath;
2425
import org.eclipse.jdt.core.ICompilationUnit;
2526
import org.eclipse.jdt.core.compiler.IProblem;
2627
import org.eclipse.jdt.core.groovy.tests.ReconcilerUtils;
28+
import org.eclipse.jdt.core.tests.builder.Problem;
2729
import org.junit.Assert;
2830
import org.junit.Test;
2931

@@ -141,6 +143,71 @@ public void testReconcilingWithTransforms_compileStatic() throws Exception {
141143
assertContainsProblem(problems, "Cannot find matching method Foo#xxx");
142144
}
143145

146+
@Test // https://issues.apache.org/jira/browse/GROOVY-10075
147+
public void testDefaultGroovyMethodFromProjectDependency() throws Exception {
148+
IPath one = env.addProject("One");
149+
env.addGroovyJars(one);
150+
151+
IPath src = env.getPackageFragmentRootPath(one, "src");
152+
env.addGroovyClass(src, "p", "X",
153+
"package p\n" +
154+
"class X {\n" +
155+
" static String getString(Iterable<String> self) {\n" +
156+
" }\n" +
157+
" static <CS extends CharSequence> CharSequence getSequence(Iterable<CS> self) {\n" +
158+
" }\n" +
159+
"}\n");
160+
env.addFile(env.addFolder(src, "META-INF/groovy"), "org.codehaus.groovy.runtime.ExtensionModule",
161+
"moduleName=ecks\n" +
162+
"moduleVersion=1.0\n" +
163+
"extensionClasses=p.X\n");
164+
165+
env.fullBuild(one);
166+
expectingNoProblemsFor(one);
167+
168+
//
169+
170+
IPath two = env.addProject("Two");
171+
env.addGroovyJars(two);
172+
env.addRequiredTestProject(two, one);
173+
src = env.getPackageFragmentRootPath(two, "src");
174+
175+
IPath bar = env.addGroovyClass(src, "foo", "Bar",
176+
"package foo\n" +
177+
"class Bar {\n" +
178+
" @groovy.transform.TypeChecked\n" +
179+
" void test() {\n" +
180+
" List<String> strings = []\n" +
181+
" strings.getSequence()\n" +
182+
" strings.getString()\n" +
183+
" strings.sequence\n" +
184+
" strings.string\n" +
185+
" }\n" +
186+
"}\n");
187+
188+
IPath baz = env.addGroovyClass(src, "foo", "Baz",
189+
"package foo\n" +
190+
"class Baz {\n" +
191+
" @groovy.transform.TypeChecked\n" +
192+
" void test() {\n" +
193+
" List<Number> numbers = []\n" +
194+
" numbers.getSequence()\n" +
195+
" numbers.getString()\n" +
196+
" numbers.sequence\n" +
197+
" numbers.string\n" +
198+
" }\n" +
199+
"}\n");
200+
201+
env.fullBuild(two);
202+
expectingNoProblemsFor(bar);
203+
expectingSpecificProblemsFor(baz, new Problem[] {
204+
new Problem("foo/Baz", "Groovy:[Static type checking] - Cannot call <CS extends java.lang.CharSequence> java.util.ArrayList#getSequence() with arguments []", baz, 106, 127, 60, IMarker.SEVERITY_ERROR),
205+
new Problem("foo/Baz", "Groovy:[Static type checking] - Cannot call java.util.ArrayList#getString() with arguments []", baz, 132, 151, 60, IMarker.SEVERITY_ERROR),
206+
new Problem("foo/Baz", "Groovy:[Static type checking] - No such property: sequence for class: java.util.ArrayList", baz, 156, 172, 60, IMarker.SEVERITY_ERROR),
207+
new Problem("foo/Baz", "Groovy:[Static type checking] - No such property: string for class: java.util.ArrayList", baz, 177, 191, 60, IMarker.SEVERITY_ERROR),
208+
});
209+
}
210+
144211
@Test // https://github.com/groovy/groovy-eclipse/issues/903
145212
public void testGlobalTransformationFromTestBuildPathEntry() throws Exception {
146213
IPath one = env.addProject("One");

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+18-2
Original file line numberDiff line numberDiff line change
@@ -1544,11 +1544,10 @@ static void addMethodLevelDeclaredGenerics(MethodNode method, Map<GenericsTypeNa
15441544
}
15451545

15461546
protected static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod) {
1547-
/* GRECLIPSE edit -- GROOVY-9902
1547+
/* GRECLIPSE edit -- GROOVY-9902, GROOVY-10075
15481548
if (isUsingUncheckedGenerics(receiver)) {
15491549
return true;
15501550
}
1551-
*/
15521551
if (CLASS_Type.equals(receiver)
15531552
&& receiver.isUsingGenerics()
15541553
&& !candidateMethod.getDeclaringClass().equals(receiver)
@@ -1568,6 +1567,23 @@ protected static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassN
15681567
} else {
15691568
return typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod, false);
15701569
}
1570+
*/
1571+
if (candidateMethod instanceof ExtensionMethodNode) {
1572+
ClassNode[] realTypes = new ClassNode[arguments.length + 1];
1573+
realTypes[0] = receiver; // object expression is first argument
1574+
System.arraycopy(arguments, 0, realTypes, 1, arguments.length);
1575+
MethodNode realMethod = ((ExtensionMethodNode) candidateMethod).getExtensionMethodNode();
1576+
return typeCheckMethodsWithGenerics(realMethod.getDeclaringClass(), realTypes, realMethod, true);
1577+
}
1578+
1579+
if (receiver.isUsingGenerics()
1580+
&& receiver.equals(CLASS_Type)
1581+
&& !candidateMethod.getDeclaringClass().equals(CLASS_Type)) {
1582+
return typeCheckMethodsWithGenerics(receiver.getGenericsTypes()[0].getType(), arguments, candidateMethod);
1583+
}
1584+
1585+
return typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod, false);
1586+
// GRECLIPSE end
15711587
}
15721588

15731589
private static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod, boolean isExtensionMethod) {

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ public void visitPropertyExpression(final PropertyExpression pexp) {
808808
if (!extension.handleUnresolvedProperty(pexp)) {
809809
Expression objectExpression = pexp.getObjectExpression();
810810
addStaticTypeError("No such property: " + pexp.getPropertyAsString() +
811-
" for class: " + findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), pexp);
811+
" for class: " + prettyPrintTypeName(findCurrentInstanceOfClass(objectExpression, getType(objectExpression))), pexp);
812812
}
813813
} finally {
814814
typeCheckingContext.popEnclosingPropertyExpression();
@@ -825,7 +825,7 @@ public void visitAttributeExpression(final AttributeExpression expression) {
825825
if (!existsProperty(expression, true) && !extension.handleUnresolvedAttribute(expression)) {
826826
Expression objectExpression = expression.getObjectExpression();
827827
addStaticTypeError("No such attribute: " + expression.getPropertyAsString() +
828-
" for class: " + findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), expression);
828+
" for class: " + prettyPrintTypeName(findCurrentInstanceOfClass(objectExpression, getType(objectExpression))), expression);
829829
}
830830
}
831831

@@ -1775,12 +1775,18 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
17751775

17761776
FieldNode field = current.getDeclaredField(propertyName);
17771777
if (field == null) {
1778+
/* GRECLIPSE edit
17781779
if (current.getSuperClass() != null) {
17791780
queue.addFirst(current.getUnresolvedSuperClass());
17801781
}
17811782
for (ClassNode face : current.getAllInterfaces()) {
17821783
queue.add(GenericsUtils.parameterizeType(current, face));
17831784
}
1785+
*/
1786+
if (current.getSuperClass() != null)
1787+
queue.addFirst(current.getSuperClass());
1788+
Collections.addAll(queue, current.getInterfaces());
1789+
// GRECLIPSE end
17841790
}
17851791

17861792
// in case of a lookup on Class we look for instance methods on Class
@@ -1879,14 +1885,18 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
18791885
}
18801886

18811887
// GROOVY-5568: the property may be defined by DGM
1882-
List<ClassNode> dgmReceivers = new ArrayList<ClassNode>(2);
1883-
dgmReceivers.add(receiverType);
1884-
if (isPrimitiveType(receiverType)) dgmReceivers.add(getWrapper(receiverType));
1885-
for (ClassNode dgmReceiver : dgmReceivers) {
1888+
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
18861889
List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
18871890
for (MethodNode m : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
18881891
if (Boolean_TYPE.equals(getWrapper(m.getReturnType()))) methods.add(m);
18891892
}
1893+
// GRECLIPSE add -- GROOVY-10075
1894+
if (isUsingGenericsOrIsArrayUsingGenerics(dgmReceiver)) {
1895+
methods.removeIf(method ->
1896+
!typeCheckMethodsWithGenerics(dgmReceiver, ClassNode.EMPTY_ARRAY, method)
1897+
);
1898+
}
1899+
// GRECLIPSE end
18901900
if (!methods.isEmpty()) {
18911901
List<MethodNode> methodNodes = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
18921902
if (methodNodes.size() == 1) {

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+18
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,7 @@ static void addMethodLevelDeclaredGenerics(final MethodNode method, final Map<Ge
14731473
}
14741474

14751475
protected static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod) {
1476+
/* GRECLIPSE edit -- GROOVY-10075
14761477
boolean isExtensionMethod = candidateMethod instanceof ExtensionMethodNode;
14771478
if (!isExtensionMethod
14781479
&& receiver.isUsingGenerics()
@@ -1492,6 +1493,23 @@ protected static boolean typeCheckMethodsWithGenerics(final ClassNode receiver,
14921493
} else {
14931494
return typeCheckMethodsWithGenerics(receiver, argumentTypes, candidateMethod, false);
14941495
}
1496+
*/
1497+
if (candidateMethod instanceof ExtensionMethodNode) {
1498+
ClassNode[] realTypes = new ClassNode[argumentTypes.length + 1];
1499+
realTypes[0] = receiver; // object expression is implicit argument
1500+
System.arraycopy(argumentTypes, 0, realTypes, 1, argumentTypes.length);
1501+
MethodNode realMethod = ((ExtensionMethodNode) candidateMethod).getExtensionMethodNode();
1502+
return typeCheckMethodsWithGenerics(realMethod.getDeclaringClass(), realTypes, realMethod, true);
1503+
}
1504+
1505+
if (receiver.isUsingGenerics()
1506+
&& receiver.equals(CLASS_Type)
1507+
&& !candidateMethod.getDeclaringClass().equals(CLASS_Type)) {
1508+
return typeCheckMethodsWithGenerics(receiver.getGenericsTypes()[0].getType(), argumentTypes, candidateMethod);
1509+
}
1510+
1511+
return typeCheckMethodsWithGenerics(receiver, argumentTypes, candidateMethod, false);
1512+
// GRECLIPSE end
14951513
}
14961514

14971515
private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod, final boolean isExtensionMethod) {

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ public void visitPropertyExpression(final PropertyExpression expression) {
701701
if (!extension.handleUnresolvedProperty(expression)) {
702702
Expression objectExpression = expression.getObjectExpression();
703703
addStaticTypeError("No such property: " + expression.getPropertyAsString() + " for class: " +
704-
findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), expression);
704+
prettyPrintTypeName(findCurrentInstanceOfClass(objectExpression, getType(objectExpression))), expression);
705705
}
706706
}
707707

@@ -712,7 +712,7 @@ public void visitAttributeExpression(final AttributeExpression expression) {
712712
if (!extension.handleUnresolvedAttribute(expression)) {
713713
Expression objectExpression = expression.getObjectExpression();
714714
addStaticTypeError("No such attribute: " + expression.getPropertyAsString() + " for class: " +
715-
findCurrentInstanceOfClass(objectExpression, getType(objectExpression)).toString(false), expression);
715+
prettyPrintTypeName(findCurrentInstanceOfClass(objectExpression, getType(objectExpression))), expression);
716716
}
717717
}
718718

@@ -1673,12 +1673,18 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
16731673

16741674
FieldNode field = current.getDeclaredField(propertyName);
16751675
if (field == null) {
1676+
/* GRECLIPSE edit
16761677
if (current.getSuperClass() != null) {
16771678
queue.addFirst(current.getUnresolvedSuperClass());
16781679
}
16791680
for (ClassNode face : current.getAllInterfaces()) {
16801681
queue.add(GenericsUtils.parameterizeType(current, face));
16811682
}
1683+
*/
1684+
if (current.getSuperClass() != null)
1685+
queue.addFirst(current.getSuperClass());
1686+
Collections.addAll(queue, current.getInterfaces());
1687+
// GRECLIPSE end
16821688
}
16831689

16841690
// in case of a lookup on Class we look for instance methods on Class
@@ -1777,15 +1783,18 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
17771783
}
17781784

17791785
// GROOVY-5568: the property may be defined by DGM
1780-
List<ClassNode> dgmReceivers = new ArrayList<>(2);
1781-
dgmReceivers.add(receiverType);
1782-
if (isPrimitiveType(receiverType))
1783-
dgmReceivers.add(getWrapper(receiverType));
1784-
for (ClassNode dgmReceiver : dgmReceivers) {
1786+
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
17851787
List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
17861788
for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
17871789
if (Boolean_TYPE.equals(getWrapper(method.getReturnType()))) methods.add(method);
17881790
}
1791+
// GRECLIPSE add -- GROOVY-10075
1792+
if (isUsingGenericsOrIsArrayUsingGenerics(dgmReceiver)) {
1793+
methods.removeIf(method ->
1794+
!typeCheckMethodsWithGenerics(dgmReceiver, ClassNode.EMPTY_ARRAY, method)
1795+
);
1796+
}
1797+
// GRECLIPSE end
17891798
if (!methods.isEmpty()) {
17901799
List<MethodNode> bestMethods = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
17911800
if (bestMethods.size() == 1) {

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+18
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,7 @@ static void addMethodLevelDeclaredGenerics(final MethodNode method, final Map<Ge
14381438
}
14391439

14401440
protected static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod) {
1441+
/* GRECLIPSE edit -- GROOVY-10075
14411442
boolean isExtensionMethod = candidateMethod instanceof ExtensionMethodNode;
14421443
if (!isExtensionMethod
14431444
&& receiver.isUsingGenerics()
@@ -1457,6 +1458,23 @@ protected static boolean typeCheckMethodsWithGenerics(final ClassNode receiver,
14571458
} else {
14581459
return typeCheckMethodsWithGenerics(receiver, argumentTypes, candidateMethod, false);
14591460
}
1461+
*/
1462+
if (candidateMethod instanceof ExtensionMethodNode) {
1463+
ClassNode[] realTypes = new ClassNode[argumentTypes.length + 1];
1464+
realTypes[0] = receiver; // object expression is implicit argument
1465+
System.arraycopy(argumentTypes, 0, realTypes, 1, argumentTypes.length);
1466+
MethodNode realMethod = ((ExtensionMethodNode) candidateMethod).getExtensionMethodNode();
1467+
return typeCheckMethodsWithGenerics(realMethod.getDeclaringClass(), realTypes, realMethod, true);
1468+
}
1469+
1470+
if (receiver.isUsingGenerics()
1471+
&& receiver.equals(CLASS_Type)
1472+
&& !candidateMethod.getDeclaringClass().equals(CLASS_Type)) {
1473+
return typeCheckMethodsWithGenerics(receiver.getGenericsTypes()[0].getType(), argumentTypes, candidateMethod);
1474+
}
1475+
1476+
return typeCheckMethodsWithGenerics(receiver, argumentTypes, candidateMethod, false);
1477+
// GRECLIPSE end
14601478
}
14611479

14621480
private static boolean typeCheckMethodsWithGenerics(final ClassNode receiver, final ClassNode[] argumentTypes, final MethodNode candidateMethod, final boolean isExtensionMethod) {

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -1597,12 +1597,9 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
15971597

15981598
FieldNode field = current.getDeclaredField(propertyName);
15991599
if (field == null) {
1600-
if (current.getSuperClass() != null) {
1601-
queue.addFirst(current.getUnresolvedSuperClass());
1602-
}
1603-
for (ClassNode face : current.getAllInterfaces()) {
1604-
queue.add(GenericsUtils.parameterizeType(current, face));
1605-
}
1600+
if (current.getSuperClass() != null)
1601+
queue.addFirst(current.getSuperClass());
1602+
Collections.addAll(queue, current.getInterfaces());
16061603
}
16071604

16081605
// in case of a lookup on Class we look for instance methods on Class
@@ -1701,15 +1698,18 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
17011698
}
17021699

17031700
// GROOVY-5568: the property may be defined by DGM
1704-
List<ClassNode> dgmReceivers = new ArrayList<>(2);
1705-
dgmReceivers.add(receiverType);
1706-
if (isPrimitiveType(receiverType))
1707-
dgmReceivers.add(getWrapper(receiverType));
1708-
for (ClassNode dgmReceiver : dgmReceivers) {
1701+
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
17091702
List<MethodNode> methods = findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "get" + capName, ClassNode.EMPTY_ARRAY);
17101703
for (MethodNode method : findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), dgmReceiver, "is" + capName, ClassNode.EMPTY_ARRAY)) {
17111704
if (Boolean_TYPE.equals(getWrapper(method.getReturnType()))) methods.add(method);
17121705
}
1706+
// GRECLIPSE add -- GROOVY-10075
1707+
if (isUsingGenericsOrIsArrayUsingGenerics(dgmReceiver)) {
1708+
methods.removeIf(method ->
1709+
!typeCheckMethodsWithGenerics(dgmReceiver, ClassNode.EMPTY_ARRAY, method)
1710+
);
1711+
}
1712+
// GRECLIPSE end
17131713
if (!methods.isEmpty()) {
17141714
List<MethodNode> bestMethods = chooseBestMethod(dgmReceiver, methods, ClassNode.EMPTY_ARRAY);
17151715
if (bestMethods.size() == 1) {

0 commit comments

Comments
 (0)