Skip to content

Commit

Permalink
GROOVY-5106
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jan 5, 2022
1 parent 9fd1b83 commit 2279366
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2021 the original author or authors.
* Copyright 2009-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -4218,8 +4218,62 @@ public void testImplementingInterface6() {
runConformTest(sources);
}

@Test // GROOVY-5687
@Test // GROOVY-5106
public void testImplementingInterface7() {
//@formatter:off
String[] sources = {
"I.groovy",
"interface I<T> {\n" +
"}\n",

"J.groovy",
"interface J<T> extends I<T> {\n" +
"}\n",

"X.groovy",
"class X implements I<String>, J<Number> {\n" +
"}\n",
};
//@formatter:on

runNegativeTest(sources,
"----------\n" +
"1. ERROR in X.groovy (at line 1)\n" +
"\tclass X implements I<String>, J<Number> {\n" +
"\t^\n" +
"Groovy:The interface I cannot be implemented more than once with different arguments: I<java.lang.String> and I<java.lang.Number>\n" +
"----------\n");
}

@Test // GROOVY-5106
public void testImplementingInterface8() {
//@formatter:off
String[] sources = {
"I.groovy",
"interface I<T> {\n" +
"}\n",

"X.groovy",
"class X implements I<String> {\n" +
"}\n",

"Y.groovy",
"class Y extends X implements I<Number> {\n" +
"}\n",
};
//@formatter:on

runNegativeTest(sources,
"----------\n" +
"1. ERROR in Y.groovy (at line 1)\n" +
"\tclass Y extends X implements I<Number> {\n" +
"\t^\n" +
"Groovy:The interface I cannot be implemented more than once with different arguments: I<java.lang.Number> and I<java.lang.String>\n" +
"----------\n");
}

@Test // GROOVY-5687
public void testImplementingInterface9() {
//@formatter:off
String[] sources = {
"Script.groovy",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.parameterizeType;
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;

/**
Expand Down Expand Up @@ -239,9 +240,11 @@ private static FieldNode getMetaClassField(ClassNode node) {
*/
public void visitClass(final ClassNode node) {
this.classNode = node;

if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode
|| classNode.isInterface()) {
// GRECLIPSE add -- GROOVY-5106
checkForDuplicateInterfaces(node);
// GRECLIPSE end
if (classNode.isInterface()
|| Traits.isTrait(node)) { // maybe possible to have this true in joint compilation mode
//interfaces have no constructors, but this code expects one,
//so create a dummy and don't add it to the class node
ConstructorNode dummy = new ConstructorNode(0, null);
Expand All @@ -252,7 +255,7 @@ public void visitClass(final ClassNode node) {
}
return;
}

/* GRECLIPSE edit -- GROOVY-5106
ClassNode[] classNodes = classNode.getInterfaces();
List<String> interfaces = new ArrayList<String>();
for (ClassNode classNode : classNodes) {
Expand All @@ -262,7 +265,7 @@ public void visitClass(final ClassNode node) {
if (interfaceSet.size() != interfaces.size()) {
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
}

*/
addDefaultParameterMethods(node);
addDefaultParameterConstructors(node);

Expand Down Expand Up @@ -318,6 +321,69 @@ public void variableNotAlwaysInitialized(final VariableExpression var) {
};
}

// GRECLIPSE add
private static void addAllInterfaces(final Set<ClassNode> result, final ClassNode source) {
for (ClassNode in : source.getInterfaces()) {
in = parameterizeType(source, in);
if (result.add(in))
addAllInterfaces(result, in);
}
ClassNode sc = source.redirect().getUnresolvedSuperClass(false);
if (sc != null && !sc.equals(ClassHelper.OBJECT_TYPE)) {
addAllInterfaces(result, parameterizeType(source, sc));
}
}

private static Set<ClassNode> getAllInterfaces(final ClassNode cn) {
Set<ClassNode> result = new HashSet<>();
if (cn.isInterface()) result.add(cn);
addAllInterfaces(result, cn);
return result;
}

private static void checkForDuplicateInterfaces(final ClassNode cn) {
ClassNode[] interfaces = cn.getInterfaces();
int nInterfaces = interfaces.length;
if (nInterfaces == 0) return;

if (nInterfaces > 1) {
List<String> interfaceNames = new ArrayList<>(nInterfaces);
for (ClassNode in : interfaces) interfaceNames.add(in.getName());
if (interfaceNames.size() != new HashSet<>(interfaceNames).size()) {
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaceNames, cn);
}
}

// GROOVY-5106: check for same interface with different type argument(s)
List< Set<ClassNode> > allInterfaces = new ArrayList<>(nInterfaces + 1);
for (ClassNode in : interfaces) allInterfaces.add(getAllInterfaces(in));
allInterfaces.add(getAllInterfaces(cn.getUnresolvedSuperClass()));
if (nInterfaces == 1 && allInterfaces.get(1).isEmpty())
return; // no peer interface(s) to verify

for (int i = 0; i < nInterfaces; i += 1) {
for (ClassNode in : allInterfaces.get(i)) {
if (in.redirect().getGenericsTypes() != null) {
for (int j = i + 1; j < nInterfaces + 1; j += 1) {
Set<ClassNode> set = allInterfaces.get(j);
if (set.contains(in)) {
for (ClassNode t : set) { // find match and check generics
if (t.equals(in)) {
String one = in.toString(false), two = t.toString(false);
if (!one.equals(two))
throw new RuntimeParserException("The interface " + in.getNameWithoutPackage() +
" cannot be implemented more than once with different arguments: " + one + " and " + two, cn);
break;
}
}
}
}
}
}
}
}
// GRECLIPSE end

private static void checkForDuplicateMethods(ClassNode cn) {
Set<String> descriptors = new HashSet<String>();
for (MethodNode mn : cn.getMethods()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
import static org.codehaus.groovy.ast.tools.GenericsUtils.parameterizeType;
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;

/**
Expand Down Expand Up @@ -230,9 +231,11 @@ private static FieldNode getMetaClassField(final ClassNode node) {
@Override
public void visitClass(final ClassNode node) {
this.classNode = node;

if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode
|| classNode.isInterface()) {
// GRECLIPSE add -- GROOVY-5106
checkForDuplicateInterfaces(node);
// GRECLIPSE end
if (classNode.isInterface()
|| Traits.isTrait(node)) { // maybe possible to have this true in joint compilation mode
//interfaces have no constructors, but this code expects one,
//so create a dummy and don't add it to the class node
ConstructorNode dummy = new ConstructorNode(0, null);
Expand All @@ -243,7 +246,7 @@ public void visitClass(final ClassNode node) {
}
return;
}

/* GRECLIPSE edit -- GROOVY-5106
ClassNode[] classNodes = classNode.getInterfaces();
List<String> interfaces = new ArrayList<>();
for (ClassNode classNode : classNodes) {
Expand All @@ -253,7 +256,7 @@ public void visitClass(final ClassNode node) {
if (interfaceSet.size() != interfaces.size()) {
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
}

*/
addDefaultParameterMethods(node);
addDefaultParameterConstructors(node);

Expand Down Expand Up @@ -308,6 +311,69 @@ public void variableNotAlwaysInitialized(final VariableExpression var) {
};
}

// GRECLIPSE add
private static void addAllInterfaces(final Set<ClassNode> result, final ClassNode source) {
for (ClassNode in : source.getInterfaces()) {
in = parameterizeType(source, in);
if (result.add(in))
addAllInterfaces(result, in);
}
ClassNode sc = source.redirect().getUnresolvedSuperClass(false);
if (sc != null && !sc.equals(ClassHelper.OBJECT_TYPE)) {
addAllInterfaces(result, parameterizeType(source, sc));
}
}

private static Set<ClassNode> getAllInterfaces(final ClassNode cn) {
Set<ClassNode> result = new HashSet<>();
if (cn.isInterface()) result.add(cn);
addAllInterfaces(result, cn);
return result;
}

private static void checkForDuplicateInterfaces(final ClassNode cn) {
ClassNode[] interfaces = cn.getInterfaces();
int nInterfaces = interfaces.length;
if (nInterfaces == 0) return;

if (nInterfaces > 1) {
List<String> interfaceNames = new ArrayList<>(nInterfaces);
for (ClassNode in : interfaces) interfaceNames.add(in.getName());
if (interfaceNames.size() != new HashSet<>(interfaceNames).size()) {
throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaceNames, cn);
}
}

// GROOVY-5106: check for same interface with different type argument(s)
List< Set<ClassNode> > allInterfaces = new ArrayList<>(nInterfaces + 1);
for (ClassNode in : interfaces) allInterfaces.add(getAllInterfaces(in));
allInterfaces.add(getAllInterfaces(cn.getUnresolvedSuperClass()));
if (nInterfaces == 1 && allInterfaces.get(1).isEmpty())
return; // no peer interface(s) to verify

for (int i = 0; i < nInterfaces; i += 1) {
for (ClassNode in : allInterfaces.get(i)) {
if (in.redirect().getGenericsTypes() != null) {
for (int j = i + 1; j < nInterfaces + 1; j += 1) {
Set<ClassNode> set = allInterfaces.get(j);
if (set.contains(in)) {
for (ClassNode t : set) { // find match and check generics
if (t.equals(in)) {
String one = in.toString(false), two = t.toString(false);
if (!one.equals(two))
throw new RuntimeParserException("The interface " + in.getNameWithoutPackage() +
" cannot be implemented more than once with different arguments: " + one + " and " + two, cn);
break;
}
}
}
}
}
}
}
}
// GRECLIPSE end

private static void checkForDuplicateMethods(final ClassNode cn) {
Set<String> descriptors = new HashSet<>();
for (MethodNode mn : cn.getMethods()) {
Expand Down
Loading

0 comments on commit 2279366

Please sign in to comment.