diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/rules/CloneMethodMustImplementCloneable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/rules/CloneMethodMustImplementCloneable.java index ff9571b355..d48fb0398a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/rules/CloneMethodMustImplementCloneable.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/rules/CloneMethodMustImplementCloneable.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.typeresolution.rules; import java.util.Arrays; import java.util.List; +import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -30,75 +31,81 @@ public class CloneMethodMustImplementCloneable extends AbstractJavaRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class); - if (impl != null && impl.jjtGetParent().equals(node)) { - for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) { - ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) impl.jjtGetChild(ix); - if (type.getType() == null) { - if ("Cloneable".equals(type.getImage())) { - return data; - } - } else if (type.getType().equals(Cloneable.class)) { - return data; - } else { - List> implementors = Arrays.asList(type.getType().getInterfaces()); - if (implementors.contains(Cloneable.class)) { - return data; - } - } - } - } - if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0) instanceof ASTExtendsList) { - ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.jjtGetChild(0).jjtGetChild(0); - Class clazz = type.getType(); - if (clazz != null && clazz.equals(Cloneable.class)) { - return data; - } - while (clazz != null && !Object.class.equals(clazz)) { - if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) { - return data; - } - clazz = clazz.getSuperclass(); - } - } + ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class); + if (impl != null && impl.jjtGetParent().equals(node)) { + for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) { + Node child = impl.jjtGetChild(ix); - return super.visit(node, data); + if (child.getClass() != ASTClassOrInterfaceType.class) { + continue; + } + + ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) child; + if (type.getType() == null) { + if ("Cloneable".equals(type.getImage())) { + return data; + } + } else if (type.getType().equals(Cloneable.class)) { + return data; + } else { + List> implementors = Arrays.asList(type.getType().getInterfaces()); + if (implementors.contains(Cloneable.class)) { + return data; + } + } + } + } + if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0) instanceof ASTExtendsList) { + ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.jjtGetChild(0).jjtGetChild(0); + Class clazz = type.getType(); + if (clazz != null && clazz.equals(Cloneable.class)) { + return data; + } + while (clazz != null && !Object.class.equals(clazz)) { + if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) { + return data; + } + clazz = clazz.getSuperclass(); + } + } + + return super.visit(node, data); } @Override public Object visit(ASTMethodDeclaration node, Object data) { - ASTClassOrInterfaceDeclaration classOrInterface = node - .getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); - if (classOrInterface != null && //Don't analyze enums, which cannot subclass clone() - (node.isFinal() || classOrInterface.isFinal())) { - if (node.findDescendantsOfType(ASTBlock.class).size() == 1) { - List blocks = node.findDescendantsOfType(ASTBlockStatement.class); - if (blocks.size() == 1) { - ASTBlockStatement block = blocks.get(0); - ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - if (type != null && type.getType() != null && type.getNthParent(9).equals(node) - && type.getType().equals(CloneNotSupportedException.class)) { - return data; - } else if (type != null && type.getType() == null - && "CloneNotSupportedException".equals(type.getImage())) { - return data; - } - } - } - } - return super.visit(node, data); + ASTClassOrInterfaceDeclaration classOrInterface = node + .getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); + if (classOrInterface != null && //Don't analyze enums, which cannot subclass clone() + (node.isFinal() || classOrInterface.isFinal())) { + if (node.findDescendantsOfType(ASTBlock.class).size() == 1) { + List blocks = node.findDescendantsOfType(ASTBlockStatement.class); + if (blocks.size() == 1) { + ASTBlockStatement block = blocks.get(0); + ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class); + if (type != null && type.getType() != null && type.getNthParent(9).equals(node) + && type.getType().equals(CloneNotSupportedException.class)) { + return data; + } else if (type != null && type.getType() == null + && "CloneNotSupportedException".equals(type.getImage())) { + return data; + } + } + } + } + return super.visit(node, data); } @Override public Object visit(ASTMethodDeclarator node, Object data) { - if (!"clone".equals(node.getImage())) { - return data; - } - int countParams = ((ASTFormalParameters) node.jjtGetChild(0)).jjtGetNumChildren(); - if (countParams != 0) { - return data; - } - addViolation(data, node); - return data; + if (!"clone".equals(node.getImage())) { + return data; + } + int countParams = ((ASTFormalParameters) node.jjtGetChild(0)).jjtGetNumChildren(); + if (countParams != 0) { + return data; + } + addViolation(data, node); + return data; } } \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/clone/xml/CloneMethodMustImplementCloneable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/clone/xml/CloneMethodMustImplementCloneable.xml index be2985b91b..b628bbefe2 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/clone/xml/CloneMethodMustImplementCloneable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/clone/xml/CloneMethodMustImplementCloneable.xml @@ -83,4 +83,12 @@ throw new CloneNotSupportedException(); } ]]> + + + #1534 [java] CloneMethodMustImplementCloneable: ClassCastException with Annotation (java8) + 0 + implements @Readonly List<@Readonly T> {} + ]]> + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/typeresolution/xml/CloneMethodMustImplementCloneable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/typeresolution/xml/CloneMethodMustImplementCloneable.xml index 6849dfb57c..2db0d188df 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/typeresolution/xml/CloneMethodMustImplementCloneable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/typeresolution/xml/CloneMethodMustImplementCloneable.xml @@ -161,4 +161,12 @@ public enum Foo { } ]]> + + + #1534 [java] CloneMethodMustImplementCloneable: ClassCastException with Annotation (java8) + 0 + implements @Readonly List<@Readonly T> {} + ]]> + diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index dad3c44683..a0d1660d4e 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -14,6 +14,7 @@ * [#103](https://github.com/pmd/pmd/pull/103): \[java] \[apex] Fix for 1501: CyclomaticComplexity rule causes OOM when class reporting is disabled * [#110](https://github.com/pmd/pmd/pull/110): \[java] Fix parser error (issue 1530) * [#111](https://github.com/pmd/pmd/pull/111): \[java] Fix BooleanInstantiationRule for Java 8 +* [#112](https://github.com/pmd/pmd/pull/112): \[java] Fix ClassCastException on CloneMethodMustImplementCloneable **Bugfixes:** @@ -26,6 +27,8 @@ * [#1522](https://sourceforge.net/p/pmd/bugs/1522/): \[java] CommentRequired: false positive * java-imports/UnusedImports * [#1529](https://sourceforge.net/p/pmd/bugs/1529/): \[java] UnusedImports: The created rule violation has no class name +* java-typeresolution/CloneMethodMustImplementCloneable + * [#1534](https://sourceforge.net/p/pmd/bugs/1534/): \[java] CloneMethodMustImplementCloneable: ClassCastException with Annotation (java8) * General * [#1499](https://sourceforge.net/p/pmd/bugs/1499/): \[core] CPD test break PMD 5.5.1 build on Windows * [#1506](https://sourceforge.net/p/pmd/bugs/1506/): \[core] When runing any RuleTst, start/end methods not called