Skip to content

Commit 2279366

Browse files
committed
GROOVY-5106
1 parent 9fd1b83 commit 2279366

File tree

4 files changed

+269
-29
lines changed
  • base
    • org.codehaus.groovy25/src/org/codehaus/groovy/classgen
    • org.codehaus.groovy30/src/org/codehaus/groovy/classgen
    • org.codehaus.groovy40/src/org/codehaus/groovy/classgen
  • base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic

4 files changed

+269
-29
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java

+56-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2021 the original author or authors.
2+
* Copyright 2009-2022 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.
@@ -4218,8 +4218,62 @@ public void testImplementingInterface6() {
42184218
runConformTest(sources);
42194219
}
42204220

4221-
@Test // GROOVY-5687
4221+
@Test // GROOVY-5106
42224222
public void testImplementingInterface7() {
4223+
//@formatter:off
4224+
String[] sources = {
4225+
"I.groovy",
4226+
"interface I<T> {\n" +
4227+
"}\n",
4228+
4229+
"J.groovy",
4230+
"interface J<T> extends I<T> {\n" +
4231+
"}\n",
4232+
4233+
"X.groovy",
4234+
"class X implements I<String>, J<Number> {\n" +
4235+
"}\n",
4236+
};
4237+
//@formatter:on
4238+
4239+
runNegativeTest(sources,
4240+
"----------\n" +
4241+
"1. ERROR in X.groovy (at line 1)\n" +
4242+
"\tclass X implements I<String>, J<Number> {\n" +
4243+
"\t^\n" +
4244+
"Groovy:The interface I cannot be implemented more than once with different arguments: I<java.lang.String> and I<java.lang.Number>\n" +
4245+
"----------\n");
4246+
}
4247+
4248+
@Test // GROOVY-5106
4249+
public void testImplementingInterface8() {
4250+
//@formatter:off
4251+
String[] sources = {
4252+
"I.groovy",
4253+
"interface I<T> {\n" +
4254+
"}\n",
4255+
4256+
"X.groovy",
4257+
"class X implements I<String> {\n" +
4258+
"}\n",
4259+
4260+
"Y.groovy",
4261+
"class Y extends X implements I<Number> {\n" +
4262+
"}\n",
4263+
};
4264+
//@formatter:on
4265+
4266+
runNegativeTest(sources,
4267+
"----------\n" +
4268+
"1. ERROR in Y.groovy (at line 1)\n" +
4269+
"\tclass Y extends X implements I<Number> {\n" +
4270+
"\t^\n" +
4271+
"Groovy:The interface I cannot be implemented more than once with different arguments: I<java.lang.Number> and I<java.lang.String>\n" +
4272+
"----------\n");
4273+
}
4274+
4275+
@Test // GROOVY-5687
4276+
public void testImplementingInterface9() {
42234277
//@formatter:off
42244278
String[] sources = {
42254279
"Script.groovy",

base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/Verifier.java

+71-5
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
125125
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
126126
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
127+
import static org.codehaus.groovy.ast.tools.GenericsUtils.parameterizeType;
127128
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
128129

129130
/**
@@ -239,9 +240,11 @@ private static FieldNode getMetaClassField(ClassNode node) {
239240
*/
240241
public void visitClass(final ClassNode node) {
241242
this.classNode = node;
242-
243-
if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode
244-
|| classNode.isInterface()) {
243+
// GRECLIPSE add -- GROOVY-5106
244+
checkForDuplicateInterfaces(node);
245+
// GRECLIPSE end
246+
if (classNode.isInterface()
247+
|| Traits.isTrait(node)) { // maybe possible to have this true in joint compilation mode
245248
//interfaces have no constructors, but this code expects one,
246249
//so create a dummy and don't add it to the class node
247250
ConstructorNode dummy = new ConstructorNode(0, null);
@@ -252,7 +255,7 @@ public void visitClass(final ClassNode node) {
252255
}
253256
return;
254257
}
255-
258+
/* GRECLIPSE edit -- GROOVY-5106
256259
ClassNode[] classNodes = classNode.getInterfaces();
257260
List<String> interfaces = new ArrayList<String>();
258261
for (ClassNode classNode : classNodes) {
@@ -262,7 +265,7 @@ public void visitClass(final ClassNode node) {
262265
if (interfaceSet.size() != interfaces.size()) {
263266
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
264267
}
265-
268+
*/
266269
addDefaultParameterMethods(node);
267270
addDefaultParameterConstructors(node);
268271

@@ -318,6 +321,69 @@ public void variableNotAlwaysInitialized(final VariableExpression var) {
318321
};
319322
}
320323

324+
// GRECLIPSE add
325+
private static void addAllInterfaces(final Set<ClassNode> result, final ClassNode source) {
326+
for (ClassNode in : source.getInterfaces()) {
327+
in = parameterizeType(source, in);
328+
if (result.add(in))
329+
addAllInterfaces(result, in);
330+
}
331+
ClassNode sc = source.redirect().getUnresolvedSuperClass(false);
332+
if (sc != null && !sc.equals(ClassHelper.OBJECT_TYPE)) {
333+
addAllInterfaces(result, parameterizeType(source, sc));
334+
}
335+
}
336+
337+
private static Set<ClassNode> getAllInterfaces(final ClassNode cn) {
338+
Set<ClassNode> result = new HashSet<>();
339+
if (cn.isInterface()) result.add(cn);
340+
addAllInterfaces(result, cn);
341+
return result;
342+
}
343+
344+
private static void checkForDuplicateInterfaces(final ClassNode cn) {
345+
ClassNode[] interfaces = cn.getInterfaces();
346+
int nInterfaces = interfaces.length;
347+
if (nInterfaces == 0) return;
348+
349+
if (nInterfaces > 1) {
350+
List<String> interfaceNames = new ArrayList<>(nInterfaces);
351+
for (ClassNode in : interfaces) interfaceNames.add(in.getName());
352+
if (interfaceNames.size() != new HashSet<>(interfaceNames).size()) {
353+
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaceNames, cn);
354+
}
355+
}
356+
357+
// GROOVY-5106: check for same interface with different type argument(s)
358+
List< Set<ClassNode> > allInterfaces = new ArrayList<>(nInterfaces + 1);
359+
for (ClassNode in : interfaces) allInterfaces.add(getAllInterfaces(in));
360+
allInterfaces.add(getAllInterfaces(cn.getUnresolvedSuperClass()));
361+
if (nInterfaces == 1 && allInterfaces.get(1).isEmpty())
362+
return; // no peer interface(s) to verify
363+
364+
for (int i = 0; i < nInterfaces; i += 1) {
365+
for (ClassNode in : allInterfaces.get(i)) {
366+
if (in.redirect().getGenericsTypes() != null) {
367+
for (int j = i + 1; j < nInterfaces + 1; j += 1) {
368+
Set<ClassNode> set = allInterfaces.get(j);
369+
if (set.contains(in)) {
370+
for (ClassNode t : set) { // find match and check generics
371+
if (t.equals(in)) {
372+
String one = in.toString(false), two = t.toString(false);
373+
if (!one.equals(two))
374+
throw new RuntimeParserException("The interface " + in.getNameWithoutPackage() +
375+
" cannot be implemented more than once with different arguments: " + one + " and " + two, cn);
376+
break;
377+
}
378+
}
379+
}
380+
}
381+
}
382+
}
383+
}
384+
}
385+
// GRECLIPSE end
386+
321387
private static void checkForDuplicateMethods(ClassNode cn) {
322388
Set<String> descriptors = new HashSet<String>();
323389
for (MethodNode mn : cn.getMethods()) {

base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/Verifier.java

+71-5
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
134134
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
135135
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
136+
import static org.codehaus.groovy.ast.tools.GenericsUtils.parameterizeType;
136137
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
137138

138139
/**
@@ -230,9 +231,11 @@ private static FieldNode getMetaClassField(final ClassNode node) {
230231
@Override
231232
public void visitClass(final ClassNode node) {
232233
this.classNode = node;
233-
234-
if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode
235-
|| classNode.isInterface()) {
234+
// GRECLIPSE add -- GROOVY-5106
235+
checkForDuplicateInterfaces(node);
236+
// GRECLIPSE end
237+
if (classNode.isInterface()
238+
|| Traits.isTrait(node)) { // maybe possible to have this true in joint compilation mode
236239
//interfaces have no constructors, but this code expects one,
237240
//so create a dummy and don't add it to the class node
238241
ConstructorNode dummy = new ConstructorNode(0, null);
@@ -243,7 +246,7 @@ public void visitClass(final ClassNode node) {
243246
}
244247
return;
245248
}
246-
249+
/* GRECLIPSE edit -- GROOVY-5106
247250
ClassNode[] classNodes = classNode.getInterfaces();
248251
List<String> interfaces = new ArrayList<>();
249252
for (ClassNode classNode : classNodes) {
@@ -253,7 +256,7 @@ public void visitClass(final ClassNode node) {
253256
if (interfaceSet.size() != interfaces.size()) {
254257
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
255258
}
256-
259+
*/
257260
addDefaultParameterMethods(node);
258261
addDefaultParameterConstructors(node);
259262

@@ -308,6 +311,69 @@ public void variableNotAlwaysInitialized(final VariableExpression var) {
308311
};
309312
}
310313

314+
// GRECLIPSE add
315+
private static void addAllInterfaces(final Set<ClassNode> result, final ClassNode source) {
316+
for (ClassNode in : source.getInterfaces()) {
317+
in = parameterizeType(source, in);
318+
if (result.add(in))
319+
addAllInterfaces(result, in);
320+
}
321+
ClassNode sc = source.redirect().getUnresolvedSuperClass(false);
322+
if (sc != null && !sc.equals(ClassHelper.OBJECT_TYPE)) {
323+
addAllInterfaces(result, parameterizeType(source, sc));
324+
}
325+
}
326+
327+
private static Set<ClassNode> getAllInterfaces(final ClassNode cn) {
328+
Set<ClassNode> result = new HashSet<>();
329+
if (cn.isInterface()) result.add(cn);
330+
addAllInterfaces(result, cn);
331+
return result;
332+
}
333+
334+
private static void checkForDuplicateInterfaces(final ClassNode cn) {
335+
ClassNode[] interfaces = cn.getInterfaces();
336+
int nInterfaces = interfaces.length;
337+
if (nInterfaces == 0) return;
338+
339+
if (nInterfaces > 1) {
340+
List<String> interfaceNames = new ArrayList<>(nInterfaces);
341+
for (ClassNode in : interfaces) interfaceNames.add(in.getName());
342+
if (interfaceNames.size() != new HashSet<>(interfaceNames).size()) {
343+
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaceNames, cn);
344+
}
345+
}
346+
347+
// GROOVY-5106: check for same interface with different type argument(s)
348+
List< Set<ClassNode> > allInterfaces = new ArrayList<>(nInterfaces + 1);
349+
for (ClassNode in : interfaces) allInterfaces.add(getAllInterfaces(in));
350+
allInterfaces.add(getAllInterfaces(cn.getUnresolvedSuperClass()));
351+
if (nInterfaces == 1 && allInterfaces.get(1).isEmpty())
352+
return; // no peer interface(s) to verify
353+
354+
for (int i = 0; i < nInterfaces; i += 1) {
355+
for (ClassNode in : allInterfaces.get(i)) {
356+
if (in.redirect().getGenericsTypes() != null) {
357+
for (int j = i + 1; j < nInterfaces + 1; j += 1) {
358+
Set<ClassNode> set = allInterfaces.get(j);
359+
if (set.contains(in)) {
360+
for (ClassNode t : set) { // find match and check generics
361+
if (t.equals(in)) {
362+
String one = in.toString(false), two = t.toString(false);
363+
if (!one.equals(two))
364+
throw new RuntimeParserException("The interface " + in.getNameWithoutPackage() +
365+
" cannot be implemented more than once with different arguments: " + one + " and " + two, cn);
366+
break;
367+
}
368+
}
369+
}
370+
}
371+
}
372+
}
373+
}
374+
}
375+
// GRECLIPSE end
376+
311377
private static void checkForDuplicateMethods(final ClassNode cn) {
312378
Set<String> descriptors = new HashSet<>();
313379
for (MethodNode mn : cn.getMethods()) {

0 commit comments

Comments
 (0)