[ 
https://issues.apache.org/jira/browse/GROOVY-11980?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18077853#comment-18077853
 ] 

ASF GitHub Bot commented on GROOVY-11980:
-----------------------------------------

Copilot commented on code in PR #2505:
URL: https://github.com/apache/groovy/pull/2505#discussion_r3177271209


##########
src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java:
##########
@@ -317,10 +317,34 @@ private static void createClone(ClassNode cNode, 
List<FieldNode> fieldNodes, Lis
         // return _result
         body.addStatement(returnS(result));
 
-        ClassNode[] exceptions = {make(CloneNotSupportedException.class)};
+        ClassNode[] exceptions = cloneExceptionsFor(cNode);
         addGeneratedMethod(cNode, "clone", ACC_PUBLIC, 
GenericsUtils.nonGeneric(cNode), Parameter.EMPTY_ARRAY, exceptions, body);
     }
 
+    /**
+     * Returns the {@code throws} clause to use for a generated {@code clone()}
+     * override. Java overrides may narrow checked exceptions but cannot add 
new
+     * ones, so the nearest superclass-declared {@code clone()} dictates the
+     * shape: if it does not declare {@link CloneNotSupportedException}, 
neither
+     * may we (e.g. a subclass of {@link java.util.HashMap}, whose {@code 
clone()}
+     * is silenced). Defaults to {@code throws CloneNotSupportedException} when
+     * no narrowing parent intervenes, matching {@link Object#clone()}.
+     */
+    static ClassNode[] cloneExceptionsFor(ClassNode cNode) {
+        for (ClassNode sc = cNode.getSuperClass(); sc != null && 
!isObjectType(sc); sc = sc.getSuperClass()) {
+            MethodNode parent = sc.getDeclaredMethod("clone", 
Parameter.EMPTY_ARRAY);
+            if (parent != null) {
+                for (ClassNode ex : parent.getExceptions()) {
+                    if 
("java.lang.CloneNotSupportedException".equals(ex.getName())) {
+                        return new 
ClassNode[]{make(CloneNotSupportedException.class)};
+                    }
+                }
+                return ClassNode.EMPTY_ARRAY;

Review Comment:
   `cloneExceptionsFor` stops at the first superclass that declares `clone()`, 
even if that method is `private` (or package-private from another package). 
Those methods are not inherited, so they do not constrain the checked 
exceptions of this override. In that case we should keep walking to the next 
visible superclass method (or `Object.clone()`), otherwise `@AutoClone` can 
incorrectly strip `CloneNotSupportedException` from subclasses whose nearest 
declared `clone()` is non-overridable.



##########
src/main/java/org/codehaus/groovy/transform/AutoCloneASTTransformation.java:
##########
@@ -317,10 +317,34 @@ private static void createClone(ClassNode cNode, 
List<FieldNode> fieldNodes, Lis
         // return _result
         body.addStatement(returnS(result));
 
-        ClassNode[] exceptions = {make(CloneNotSupportedException.class)};
+        ClassNode[] exceptions = cloneExceptionsFor(cNode);
         addGeneratedMethod(cNode, "clone", ACC_PUBLIC, 
GenericsUtils.nonGeneric(cNode), Parameter.EMPTY_ARRAY, exceptions, body);
     }
 
+    /**
+     * Returns the {@code throws} clause to use for a generated {@code clone()}
+     * override. Java overrides may narrow checked exceptions but cannot add 
new
+     * ones, so the nearest superclass-declared {@code clone()} dictates the
+     * shape: if it does not declare {@link CloneNotSupportedException}, 
neither
+     * may we (e.g. a subclass of {@link java.util.HashMap}, whose {@code 
clone()}
+     * is silenced). Defaults to {@code throws CloneNotSupportedException} when
+     * no narrowing parent intervenes, matching {@link Object#clone()}.
+     */
+    static ClassNode[] cloneExceptionsFor(ClassNode cNode) {

Review Comment:
   This PR adds a new code path for classes whose superclass narrows away 
`CloneNotSupportedException` (the `HashMap` case from GROOVY-11980), but there 
isn't a regression test covering that path. The existing `@AutoClone` tests 
only exercise the default `throws CloneNotSupportedException` signature, so a 
future change could easily break the new runtime/stub behavior without being 
caught.





> @AutoClone clone() override adds CloneNotSupportedException not declared by 
> superclass
> --------------------------------------------------------------------------------------
>
>                 Key: GROOVY-11980
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11980
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to