This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new a3c5939497 GROOVY-11550: check method for name clash (same erasure,
different type)
a3c5939497 is described below
commit a3c59394977fd9feb5220812f62b6fd0b60a0739
Author: Eric Milles <[email protected]>
AuthorDate: Sun Mar 9 16:30:12 2025 -0500
GROOVY-11550: check method for name clash (same erasure, different type)
---
.../apache/groovy/ast/tools/ClassNodeUtils.java | 16 ++--
.../apache/groovy/ast/tools/MethodNodeUtils.java | 48 ++++++++---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 2 +-
.../groovy/classgen/ClassCompletionVerifier.java | 94 +++++++++++++++++++---
.../org/codehaus/groovy/classgen/EnumVisitor.java | 2 +-
.../org/codehaus/groovy/classgen/Verifier.java | 38 +++++----
.../transform/trait/TraitASTTransformation.java | 2 +
.../codehaus/groovy/transform/trait/Traits.java | 16 ++--
src/test/gls/invocation/CovariantReturnTest.groovy | 36 ++++++---
src/test/groovy/OverrideTest.groovy | 18 ++---
src/test/groovy/bugs/Groovy10236.groovy | 4 +-
src/test/groovy/bugs/Groovy10281.groovy | 2 +-
src/test/groovy/bugs/Groovy10305.groovy | 2 +-
src/test/groovy/bugs/Groovy10381.groovy | 2 +-
src/test/groovy/bugs/Groovy11272.groovy | 2 +-
src/test/groovy/bugs/Groovy11292.groovy | 2 +-
src/test/groovy/bugs/Groovy11293.groovy | 2 +-
.../transform/TupleConstructorTransformTest.groovy | 4 +-
.../traitx/TraitASTTransformationTest.groovy | 4 +-
19 files changed, 206 insertions(+), 90 deletions(-)
diff --git a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
index e1d848ee2b..e91bc8f651 100644
--- a/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/ClassNodeUtils.java
@@ -75,20 +75,20 @@ public class ClassNodeUtils {
*/
public static String formatTypeName(final ClassNode cNode) {
if (cNode.isArray()) {
- ClassNode it = cNode;
int dim = 0;
- while (it.isArray()) {
- dim++;
- it = it.getComponentType();
+ ClassNode cn = cNode;
+ while (cn.isArray()) {
+ dim += 1;
+ cn = cn.getComponentType();
}
- StringBuilder sb = new StringBuilder(it.getName().length() + 2 *
dim);
- sb.append(it.getName());
- for (int i = 0; i < dim; i++) {
+ StringBuilder sb = new StringBuilder(cn.getName().length() + (2 *
dim));
+ sb.append(formatTypeName(cn));
+ for (int i = 0; i < dim; i += 1) {
sb.append("[]");
}
return sb.toString();
}
- return cNode.getName();
+ return cNode.isGenericsPlaceHolder() ? cNode.getUnresolvedName() :
cNode.getName();
}
/**
diff --git a/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
b/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
index 40b7f83fb2..3c5de04919 100644
--- a/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
+++ b/src/main/java/org/apache/groovy/ast/tools/MethodNodeUtils.java
@@ -18,6 +18,7 @@
*/
package org.apache.groovy.ast.tools;
+import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
@@ -40,6 +41,14 @@ public class MethodNodeUtils {
private MethodNodeUtils() { }
+ private static void appendTypeName(final StringBuilder sb, ClassNode cn) {
+ while (cn.isArray()) {
+ cn = cn.getComponentType();
+ sb.append('[');
+ }
+ sb.append(cn.getName());
+ }
+
/**
* Return the method node's descriptor including its
* name and parameter types without generics.
@@ -51,7 +60,8 @@ public class MethodNodeUtils {
StringBuilder sb = new StringBuilder();
sb.append(mNode.getName()).append(':');
for (Parameter p : mNode.getParameters()) {
- sb.append(ClassNodeUtils.formatTypeName(p.getType())).append(',');
+ appendTypeName(sb, p.getType());
+ sb.append(';');
}
return sb.toString();
}
@@ -75,26 +85,38 @@ public class MethodNodeUtils {
* @param pretty whether to quote a name with spaces
* @return the method node's descriptor
*/
- public static String methodDescriptor(final MethodNode mNode, boolean
pretty) {
+ public static String methodDescriptor(final MethodNode mNode, final
boolean pretty) {
String name = mNode.getName();
- if (pretty) pretty = name.contains(" ");
Parameter[] parameters = mNode.getParameters();
- int nParameters = parameters == null ? 0 : parameters.length;
-
- StringBuilder sb = new StringBuilder(name.length() * 2 + nParameters *
10);
- sb.append(ClassNodeUtils.formatTypeName(mNode.getReturnType()));
- sb.append(' ');
- if (pretty) sb.append('"');
- sb.append(name);
- if (pretty) sb.append('"');
+ int nParameters = (parameters == null ? 0 : parameters.length);
+
+ StringBuilder sb = new StringBuilder((name.length() * 2) +
(nParameters * 10));
+ if (pretty && !mNode.isConstructor()) {
+ sb.append(ClassNodeUtils.formatTypeName(mNode.getReturnType()));
+ sb.append(' ');
+ }
+ if (name.contains(" ")) {
+ sb.append('"').append(name).append('"');
+ } else {
+ sb.append(name);
+ }
sb.append('(');
for (int i = 0; i < nParameters; i += 1) {
if (i > 0) {
- sb.append(", ");
+ sb.append(',');
+ if (pretty) sb.append(' ');
+ }
+ if (pretty) {
+
sb.append(ClassNodeUtils.formatTypeName(parameters[i].getType()));
+ } else {
+ appendTypeName(sb, parameters[i].getType());
}
- sb.append(ClassNodeUtils.formatTypeName(parameters[i].getType()));
}
sb.append(')');
+ if (!pretty && !mNode.isConstructor()) {
+ sb.append(':');
+ appendTypeName(sb, mNode.getReturnType());
+ }
return sb.toString();
}
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index aa5ced7702..908974881b 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -597,7 +597,7 @@ public class GenericsUtils {
} else {
ClassNode superClass = getSuperClass(type, target);
if (superClass != null) {
- if (hasUnresolvedGenerics(superClass)) {
+ if (type.isRedirectNode() &&
hasUnresolvedGenerics(superClass)) {
GenericsType[] tp = type.redirect().getGenericsTypes();
if (tp != null) {
GenericsType[] ta = type.getGenericsTypes();
diff --git
a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index 100118cdd5..845c0dc5b3 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.classgen;
import groovy.transform.Sealed;
import org.apache.groovy.ast.tools.AnnotatedNodeUtils;
import org.apache.groovy.ast.tools.ClassNodeUtils;
+import org.apache.groovy.ast.tools.MethodNodeUtils;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassHelper;
@@ -55,6 +56,7 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isNative;
@@ -65,6 +67,10 @@ import static java.lang.reflect.Modifier.isStrict;
import static java.lang.reflect.Modifier.isSynchronized;
import static java.lang.reflect.Modifier.isTransient;
import static java.lang.reflect.Modifier.isVolatile;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.buildWildcardType;
+import static
org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpecRecurse;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_NATIVE;
@@ -123,6 +129,7 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
checkMethodsForIncorrectName(node);
checkMethodsForWeakerAccess(node);
checkMethodsForOverridingFinal(node);
+ checkMethodsForOverridingIssue(node);
checkNoAbstractMethodsNonAbstractClass(node);
checkClassExtendsOrImplementsSelfTypes(node);
checkNoStaticMethodWithSameSignatureAsNonStatic(node);
@@ -279,7 +286,7 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
}
private static String getDescription(final MethodNode node) {
- return "method '" + node.getTypeDescriptor() + "'";
+ return "method '" + MethodNodeUtils.methodDescriptor(node, true) + "'";
}
private static String getDescription(final FieldNode node) {
@@ -299,7 +306,7 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
addError("Can't have an abstract method in a non-abstract class." +
" The " + getDescription(currentClass) + " must be declared
abstract or the method '" +
- methodNode.getTypeDescriptor() + "' must not be abstract.",
methodNode);
+ MethodNodeUtils.methodDescriptor(methodNode, true) + "' must
not be abstract.", methodNode);
}
private void checkClassForExtendingFinalOrSealed(final ClassNode cn) {
@@ -404,28 +411,97 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
}
private void checkMethodsForOverridingFinal(final ClassNode cn) {
+ final int skips = ACC_SYNTHETIC | ACC_STATIC | ACC_PRIVATE;
for (MethodNode method : cn.getMethods()) {
- if ((method.getModifiers() & ACC_SYNTHETIC) != 0) continue; //
GROOVY-11579: bridge method
+ if ((method.getModifiers() & skips) != 0) continue; // GROOVY-11579
- ClassNode sc = cn.getSuperClass();
Parameter[] params = method.getParameters();
- for (MethodNode superMethod : sc.getMethods(method.getName())) {
- if (superMethod.isFinal()
+ for (MethodNode superMethod :
cn.getSuperClass().getMethods(method.getName())) {
+ if ((superMethod.getModifiers() & skips + ACC_FINAL) ==
ACC_FINAL
&& ParameterUtils.parametersEqual(params,
superMethod.getParameters())) {
StringBuilder sb = new StringBuilder();
sb.append("You are not allowed to override the final
method ");
- sb.append(method.getName());
+ if (method.getName().contains(" ")) {
+ sb.append('"').append(method.getName()).append('"');
+ } else {
+ sb.append(method.getName());
+ }
appendParamsDescription(params, sb);
sb.append(" from ");
- sb.append(getDescription(sc));
+ sb.append(getDescription(superMethod.getDeclaringClass()));
sb.append(".");
addError(sb.toString(), method.getLineNumber() > 0 ?
method : cn);
+ break;
}
}
}
}
+ private void checkMethodsForOverridingIssue(final ClassNode cn) {
+ Set<ClassNode> superTypes = getAllSuperTypes(cn);
+ superTypes.remove(ClassHelper.GROOVY_OBJECT_TYPE);
+ superTypes.remove(ClassHelper.OBJECT_TYPE);
+ if (superTypes.isEmpty()) return;
+
+ for (MethodNode mn : cn.getMethods()) {
+ Parameter[] pa = mn.getParameters();
+ if (pa.length == 0 || (mn.getModifiers() & ACC_SYNTHETIC +
ACC_STATIC + ACC_PRIVATE) != 0) continue;
+
+out: for (ClassNode sc : superTypes) {
+ Map<String, ClassNode> cspec = createGenericsSpec(sc);
+ for (MethodNode sm : sc.getDeclaredMethods(mn.getName())) {
+ if (!sm.isStatic() && !sm.isPrivate() &&
ParameterUtils.parametersEqual(pa, sm.getParameters())) {
+ Map<String, ClassNode> mspec = addMethodGenerics(sm,
cspec);
+ for (int i = 0, n = pa.length; i < n; i += 1) {
+ var t0 = sm.getParameters()[i].getType();
+ var t1 = pa[i].getType();
+ if (!t0.isGenericsPlaceHolder() &&
!ClassHelper.isPrimitiveType(t0)
+ && !t1.isGenericsPlaceHolder() &&
!ClassHelper.isPrimitiveType(t1)
+ && !buildWildcardType(t0 =
correctToGenericsSpecRecurse(mspec, t0)).isCompatibleWith(t1)) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("name clash: ");
+ if (mn.getName().contains(" ")) {
+
sb.append('"').append(mn.getName()).append('"');
+ } else {
+ sb.append(mn.getName());
+ }
+ appendParamsDescription(pa, sb);
+ sb.append(" in ");
+ sb.append(getDescription(cn));
+ sb.append(" and ");
+ if (sm.getName().contains(" ")) {
+
sb.append('"').append(sm.getName()).append('"');
+ } else {
+ sb.append(sm.getName());
+ }
+ appendParamsDescription(sm.getParameters(),
sb);
+ sb.append(" in ");
+ sb.append(getDescription(sc));
+ sb.append(" have the same erasure, yet neither
overrides the other.");
+
+ addError(sb.toString(), mn.getLineNumber() > 0
? mn : cn);
+ break;
+ }
+ }
+ break out;
+ }
+ }
+ }
+ }
+ }
+
+ private static Set<ClassNode> getAllSuperTypes(ClassNode cn) {
+ Set<ClassNode> interfaces =
GeneralUtils.getInterfacesAndSuperInterfaces(cn);
+ Set<ClassNode> superTypes = new LinkedHashSet<>();
+ interfaces.remove(cn);
+ while ((cn = cn.getSuperClass()) != null) {
+ superTypes.add(cn);
+ }
+ superTypes.addAll(interfaces);
+ return superTypes;
+ }
+
private void appendParamsDescription(final Parameter[] parameters, final
StringBuilder msg) {
msg.append('(');
boolean needsComma = false;
@@ -435,7 +511,7 @@ public class ClassCompletionVerifier extends
ClassCodeVisitorSupport {
} else {
needsComma = true;
}
- msg.append(parameter.getType());
+ msg.append(parameter.getType().toString(false));
}
msg.append(')');
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
index d73339304f..ab22c7ca25 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
@@ -285,7 +285,7 @@ public class EnumVisitor extends ClassCodeVisitorSupport {
if (!methodNode.isAbstract()) continue;
MethodNode enumConstMethod =
inner.getMethod(methodNode.getName(), methodNode.getParameters());
if (enumConstMethod == null ||
enumConstMethod.isAbstract()) {
- addError(field, "Can't have an abstract method
in enum constant " + field.getName() + ". Implement method '" +
methodNode.getTypeDescriptor() + "'.");
+ addError(field, "Can't have an abstract method
in enum constant " + field.getName() + ". Implement method '" +
methodNode.getTypeDescriptor(true) + "'.");
}
}
if (inner.getVariableScope() == null) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 29bc833155..f45013210c 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -98,9 +98,6 @@ import static
org.apache.groovy.ast.tools.AnnotatedNodeUtils.isGenerated;
import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.markAsGenerated;
import static
org.apache.groovy.ast.tools.ConstructorNodeUtils.getFirstIfSpecialConstructorCall;
import static
org.apache.groovy.ast.tools.ExpressionUtils.transformInlineConstants;
-import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
-import static org.apache.groovy.ast.tools.MethodNodeUtils.getPropertyName;
-import static
org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
import static org.codehaus.groovy.ast.ClassHelper.isObjectType;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveDouble;
@@ -291,7 +288,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
.flatMap(i -> i.getAllDeclaredMethods().stream())
.filter(MethodNode::isDefault)
.forEach(m -> {
- String signature = methodDescriptorWithoutReturnType(m);
+ String signature =
MethodNodeUtils.methodDescriptorWithoutReturnType(m);
if (declared.contains(signature)) {
return;
}
@@ -308,7 +305,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
||
currentDeclaringClass.implementsInterface(existingDeclaringClass))) {
throw new RuntimeParserException(
(node.isInterface() ? "interface" : "class") +
" " + node.getName()
- + " inherits unrelated defaults for "
+ m.getTypeDescriptor()
+ + " inherits unrelated defaults for "
+ MethodNodeUtils.methodDescriptor(m, true)
+ " from types " +
existingDeclaringClass.getName()
+ " and " +
currentDeclaringClass.getName(), node);
}
@@ -427,7 +424,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
Set<String> descriptors = new HashSet<>();
for (MethodNode mn : cn.getMethods()) {
if (mn.isSynthetic()) continue;
- String mySig = methodDescriptorWithoutReturnType(mn);
+ String mySig =
MethodNodeUtils.methodDescriptorWithoutReturnType(mn);
if (descriptors.contains(mySig)) {
if (mn.isScriptBody() ||
mySig.equals(scriptBodySignatureWithoutReturnType(cn))) {
throw new RuntimeParserException("The method " +
mn.getText() +
@@ -444,7 +441,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
private static void checkForDuplicateConstructors(final ClassNode cn) {
Set<String> descriptors = new HashSet<>();
for (ConstructorNode cons : cn.getDeclaredConstructors()) {
- String mySig = methodDescriptorWithoutReturnType(cons);
+ String mySig =
MethodNodeUtils.methodDescriptorWithoutReturnType(cons);
if (descriptors.contains(mySig)) {
throw new RuntimeParserException("The constructor " +
cons.getText() +
" duplicates another constructor of the same signature",
sourceOf(cons));
@@ -455,7 +452,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
private static String scriptBodySignatureWithoutReturnType(final ClassNode
cn) {
for (MethodNode mn : cn.getMethods()) {
- if (mn.isScriptBody()) return
methodDescriptorWithoutReturnType(mn);
+ if (mn.isScriptBody()) return
MethodNodeUtils.methodDescriptorWithoutReturnType(mn);
}
return null;
}
@@ -921,8 +918,8 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
MethodNode oldMethod = type.getDeclaredMethod(method.getName(),
params);
if (oldMethod != null) {
throw new RuntimeParserException(
- "The method with default parameters \"" +
method.getTypeDescriptor() +
- "\" defines a method \"" +
newMethod.getTypeDescriptor() +
+ "The method with default parameters \"" +
MethodNodeUtils.methodDescriptor(method, true) +
+ "\" defines a method \"" +
MethodNodeUtils.methodDescriptor(newMethod, true) +
"\" that is already defined.",
sourceOf(method));
}
@@ -1057,7 +1054,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
addConstructor(params, (ConstructorNode) method,
stmt(ctorThisX(arguments)), type);
} else {
String warning = "Default argument(s) specify duplicate
constructor: " +
- old.getTypeDescriptor().replace("void <init>",
type.getNameWithoutPackage());
+
MethodNodeUtils.methodDescriptor(old,true).replace("<init>",type.getNameWithoutPackage());
type.getModule().getContext().addWarning(warning,
method.getLineNumber() > 0 ? method : type);
}
});
@@ -1183,7 +1180,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
}
}
- BlockStatement block = getCodeAsBlock(constructorNode);
+ BlockStatement block = MethodNodeUtils.getCodeAsBlock(constructorNode);
List<Statement> blockStatements = block.getStatements();
if (!blockStatements.isEmpty()) {
if (specialCtorCall != null) {
@@ -1541,7 +1538,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
if (ignoreError) return null;
throw new RuntimeParserException(
"The return type of " +
- overrideCandidate.getTypeDescriptor() +
+
MethodNodeUtils.methodDescriptor(overrideCandidate, true) +
" in " +
overrideCandidate.getDeclaringClass().getName() +
" is incompatible with " + omrCorrected.getName() +
" in " + oldMethod.getDeclaringClass().getName(),
@@ -1553,14 +1550,14 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
if (oldMethod.isFinal()) {
throw new RuntimeParserException(
"Cannot override final method " +
- oldMethod.getTypeDescriptor() +
+ MethodNodeUtils.methodDescriptor(oldMethod, true) +
" in " + oldMethod.getDeclaringClass().getName(),
sourceOf(overrideCandidate));
}
if (oldMethod.isStatic() != overrideCandidate.isStatic()) {
throw new RuntimeParserException(
"Cannot override method " +
- oldMethod.getTypeDescriptor() +
+ MethodNodeUtils.methodDescriptor(oldMethod, true) +
" in " + oldMethod.getDeclaringClass().getName() +
" with disparate static modifier",
sourceOf(overrideCandidate));
@@ -1579,7 +1576,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
}
throw new RuntimeParserException(
"Cannot override method " +
- oldMethod.getTypeDescriptor() +
+ MethodNodeUtils.methodDescriptor(oldMethod,
true) +
" in " +
oldMethod.getDeclaringClass().getName() +
message,
sourceOf(overrideCandidate));
@@ -1661,9 +1658,10 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
}
private static ClassNode cleanType(final ClassNode type) {
- // TODO: Should this be directly handled by getPlainNodeReference?
- if (type.isArray()) return
cleanType(type.getComponentType()).makeArray();
- return type.getPlainNodeReference();
+ if (type.isArray()) {
+ return cleanType(type.getComponentType()).makeArray();
+ }
+ return type.redirect().getPlainNodeReference();
}
private static boolean equalParametersNormal(final MethodNode m1, final
MethodNode m2) {
@@ -1729,7 +1727,7 @@ public class Verifier implements GroovyClassVisitor,
Opcodes {
if (methodNode.getLineNumber() < 1) {
ClassNode declaringClass = methodNode.getDeclaringClass();
if (methodNode.isSynthetic()) {
- String propertyName = getPropertyName(methodNode);
+ String propertyName =
MethodNodeUtils.getPropertyName(methodNode);
if (propertyName != null) {
PropertyNode propertyNode =
declaringClass.getProperty(propertyName);
if (propertyNode != null && propertyNode.getLineNumber() >
0) {
diff --git
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
index e7c36a2a58..dde69b9efe 100644
---
a/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
+++
b/src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java
@@ -194,6 +194,7 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
ClassNode.EMPTY_ARRAY,
null
);
+ helper.setGenericsTypes(cNode.getGenericsTypes());
helper.setStaticClass(true); // GROOVY-7242, GROOVY-7456, etc.
MethodNode initializer = createInitMethod(false, helper);
@@ -226,6 +227,7 @@ public class TraitASTTransformation extends
AbstractASTTransformation implements
ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE |
ACC_SYNTHETIC,
OBJECT_TYPE
);
+ fieldHelper.setGenericsTypes(cNode.getGenericsTypes());
fieldHelper.setStaticClass(true);
if (hasStatic) {
staticFieldHelper = new InnerClassNode(
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
b/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
index 8a99930e39..31ca6a88e2 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/Traits.java
@@ -26,7 +26,7 @@ import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
-import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.GenericsType;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.Expression;
@@ -42,7 +42,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -130,10 +129,10 @@ public abstract class Traits {
ClassNode helperClassNode = null;
ClassNode fieldHelperClassNode = null;
ClassNode staticFieldHelperClassNode = null;
- Iterator<InnerClassNode> innerClasses =
trait.redirect().getInnerClasses();
- if (innerClasses != null && innerClasses.hasNext()) {
+ var innerClasses = trait.redirect().getInnerClasses();
+ if (innerClasses != null && innerClasses.hasNext() ) {
// trait declared in same unit
- while (innerClasses.hasNext()) {
+ do {
ClassNode icn = innerClasses.next();
if (icn.getName().endsWith(Traits.TRAIT_HELPER)) {
helperClassNode = icn;
@@ -142,7 +141,7 @@ public abstract class Traits {
} else if (icn.getName().endsWith(Traits.STATIC_FIELD_HELPER))
{
staticFieldHelperClassNode = icn;
}
- }
+ } while (innerClasses.hasNext());
} else {
// precompiled trait
try {
@@ -159,6 +158,11 @@ public abstract class Traits {
throw new GroovyBugError("Couldn't find trait helper classes
on compile classpath!", e);
}
}
+ GenericsType[] typeArguments = trait.getGenericsTypes();
+ helperClassNode = GenericsUtils.makeClassSafe0(helperClassNode,
typeArguments);
+ if (fieldHelperClassNode != null) {
+ fieldHelperClassNode =
GenericsUtils.makeClassSafe0(fieldHelperClassNode, typeArguments);
+ }
return new TraitHelpersTuple(helperClassNode, fieldHelperClassNode,
staticFieldHelperClassNode);
}
diff --git a/src/test/gls/invocation/CovariantReturnTest.groovy
b/src/test/gls/invocation/CovariantReturnTest.groovy
index b67f87d2e2..acef4438b9 100644
--- a/src/test/gls/invocation/CovariantReturnTest.groovy
+++ b/src/test/gls/invocation/CovariantReturnTest.groovy
@@ -237,17 +237,17 @@ final class CovariantReturnTest {
@Test
void testCovariantArrayReturnType1() {
assertScript '''
- interface Base {}
-
- interface Derived extends Base {}
-
+ interface A {
+ }
+ interface B extends A {
+ }
interface I {
- Base[] foo()
+ A[] foo()
}
-
class C implements I {
- Derived[] foo() { null }
+ B[] foo() { null }
}
+
new C().foo()
'''
}
@@ -257,26 +257,42 @@ final class CovariantReturnTest {
void testCovariantArrayReturnType2() {
assertScript '''
interface A<T> {
- T[] process();
+ T[] process()
}
-
class B implements A<String> {
@Override
public String[] process() {
['foo']
}
}
-
class C extends B {
@Override
String[] process() {
super.process()
}
}
+
assert new C().process()[0] == 'foo'
'''
}
+ // GROOVY-11579
+ @Test
+ void testCovariantBridgeReturnType() {
+ assertScript '''
+ interface I<T> {
+ T m()
+ }
+ abstract class A {
+ final String m() { 'A' }
+ }
+ class C extends A implements I<String> {
+ }
+
+ assert new C().m() == 'A'
+ '''
+ }
+
// GROOVY-7495
@Test
void testCovariantReturnFromIndirectInterface() {
diff --git a/src/test/groovy/OverrideTest.groovy
b/src/test/groovy/OverrideTest.groovy
index bbca486049..dfc658f583 100644
--- a/src/test/groovy/OverrideTest.groovy
+++ b/src/test/groovy/OverrideTest.groovy
@@ -225,21 +225,19 @@ final class OverrideTest {
'''
}
- // GROOVY-11579
+ // GROOVY-11550
@Test
- void testCovariantReturnType() {
- assertScript '''
+ void testCovariantParameterType6() {
+ def err = shouldFail '''
interface I<T> {
- T m()
- }
- abstract class A {
- final String m() { 'A' }
+ void m(I<Object> i_of_object)
}
- class C extends A implements I<String> {
+ class C implements I<Object> {
+ void m(I<String> i_of_string) {
+ }
}
-
- assert new C().m() == 'A'
'''
+ assert err =~ /name clash: m\(I<java.lang.String>\) in class 'C' and
m\(I<java.lang.Object>\) in interface 'I' have the same erasure, yet neither
overrides the other./
}
@Test
diff --git a/src/test/groovy/bugs/Groovy10236.groovy
b/src/test/groovy/bugs/Groovy10236.groovy
index 2ffac11c0b..2747b43aae 100644
--- a/src/test/groovy/bugs/Groovy10236.groovy
+++ b/src/test/groovy/bugs/Groovy10236.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import groovy.transform.CompileStatic
import org.junit.Test
@@ -30,7 +30,7 @@ final class Groovy10236 {
void testOmittingParenthesesInLambdaBody() {
assertScript '''
def same(obj) { obj }
-
+
assert ['1', '2', '3'] == [1, 2, 3].stream().map(num -> same
"$num").toList()
'''
}
diff --git a/src/test/groovy/bugs/Groovy10281.groovy
b/src/test/groovy/bugs/Groovy10281.groovy
index 9f59928d44..e3b916b76a 100644
--- a/src/test/groovy/bugs/Groovy10281.groovy
+++ b/src/test/groovy/bugs/Groovy10281.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import groovy.transform.CompileStatic
import org.codehaus.groovy.control.CompilationUnit
diff --git a/src/test/groovy/bugs/Groovy10305.groovy
b/src/test/groovy/bugs/Groovy10305.groovy
index db2a9b5d25..28ea4ecb98 100644
--- a/src/test/groovy/bugs/Groovy10305.groovy
+++ b/src/test/groovy/bugs/Groovy10305.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import org.junit.Test
diff --git a/src/test/groovy/bugs/Groovy10381.groovy
b/src/test/groovy/bugs/Groovy10381.groovy
index 568bb9b87c..8a0138efd2 100644
--- a/src/test/groovy/bugs/Groovy10381.groovy
+++ b/src/test/groovy/bugs/Groovy10381.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
diff --git a/src/test/groovy/bugs/Groovy11272.groovy
b/src/test/groovy/bugs/Groovy11272.groovy
index 237359d66c..2ffea4a110 100644
--- a/src/test/groovy/bugs/Groovy11272.groovy
+++ b/src/test/groovy/bugs/Groovy11272.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import org.junit.Test
diff --git a/src/test/groovy/bugs/Groovy11292.groovy
b/src/test/groovy/bugs/Groovy11292.groovy
index b4421e7800..d6acfd9dc1 100644
--- a/src/test/groovy/bugs/Groovy11292.groovy
+++ b/src/test/groovy/bugs/Groovy11292.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
diff --git a/src/test/groovy/bugs/Groovy11293.groovy
b/src/test/groovy/bugs/Groovy11293.groovy
index 37ee96e9d0..6c1cf3d750 100644
--- a/src/test/groovy/bugs/Groovy11293.groovy
+++ b/src/test/groovy/bugs/Groovy11293.groovy
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package bugs
+package groovy.bugs
import org.junit.Test
diff --git
a/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
b/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
index b790f9208c..790592a7e8 100644
---
a/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
+++
b/src/test/org/codehaus/groovy/transform/TupleConstructorTransformTest.groovy
@@ -373,8 +373,8 @@ final class TupleConstructorTransformTest {
@ASTTest(phase=CANONICALIZATION, value={
assert node.constructors.size() == 2
node.constructors.each {
- assert (it.typeDescriptor == 'void
<init>(java.lang.String)' && isProtected(it.modifiers)) ||
- (it.typeDescriptor == 'void <init>(int)' &&
isPrivate(it.modifiers))
+ assert (it.typeDescriptor == '<init>(java.lang.String)' &&
isProtected(it.modifiers))
+ || (it.typeDescriptor == '<init>(int)' &&
isPrivate(it.modifiers))
}
})
class Person {
diff --git
a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index b09550f321..3c8c2445ab 100644
---
a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++
b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -3238,12 +3238,12 @@ final class TraitASTTransformationTest {
def mn = cn.getMethods('m')[0]
def td = mn.typeDescriptor
- assert td == 'java.lang.Object m(java.lang.Class,
java.lang.Object[])'
+ assert td ==
'm(java.lang.Class,[java.lang.Object):java.lang.Object'
"""
System.setProperty('spock.iKnowWhatImDoing.disableGroovyVersionCheck','true')
assertScript shell, """
- @Grab('org.spockframework:spock-core:2.4-M1-groovy-4.0')
+ @Grab('org.spockframework:spock-core:2.4-M5-groovy-4.0')
@GrabExclude('org.apache.groovy:*')
import spock.lang.Specification