Fix ClassCastException on CloneMethodMustImplementCloneable

- Java 8 code allows for things such as
    `class UnmodifiableList<T> implements @Readonly List<@Readonly T> {}`
    where not all token in the ASTImplementsList are ASTClassOrInterfaceType
This commit is contained in:
Juan Martín Sotuyo Dodero
2016-10-12 18:01:03 -03:00
committed by Andreas Dangel
parent f4f2402661
commit 238f6b721b

View File

@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.typeresolution.rules;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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.ASTBlock;
import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
@ -30,75 +31,81 @@ public class CloneMethodMustImplementCloneable extends AbstractJavaRule {
@Override @Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class); ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class);
if (impl != null && impl.jjtGetParent().equals(node)) { if (impl != null && impl.jjtGetParent().equals(node)) {
for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) { for (int ix = 0; ix < impl.jjtGetNumChildren(); ix++) {
ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) impl.jjtGetChild(ix); Node child = impl.jjtGetChild(ix);
if (type.getType() == null) {
if ("Cloneable".equals(type.getImage())) { if (child.getClass() != ASTClassOrInterfaceType.class) {
return data; continue;
} }
} else if (type.getType().equals(Cloneable.class)) {
return data; ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) child;
} else { if (type.getType() == null) {
List<Class<?>> implementors = Arrays.asList(type.getType().getInterfaces()); if ("Cloneable".equals(type.getImage())) {
if (implementors.contains(Cloneable.class)) { return data;
return data; }
} } else if (type.getType().equals(Cloneable.class)) {
} return data;
} } else {
} List<Class<?>> implementors = Arrays.asList(type.getType().getInterfaces());
if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0) instanceof ASTExtendsList) { if (implementors.contains(Cloneable.class)) {
ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.jjtGetChild(0).jjtGetChild(0); return data;
Class<?> clazz = type.getType(); }
if (clazz != null && clazz.equals(Cloneable.class)) { }
return data; }
} }
while (clazz != null && !Object.class.equals(clazz)) { if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0) instanceof ASTExtendsList) {
if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) { ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.jjtGetChild(0).jjtGetChild(0);
return data; Class<?> clazz = type.getType();
} if (clazz != null && clazz.equals(Cloneable.class)) {
clazz = clazz.getSuperclass(); return data;
} }
} while (clazz != null && !Object.class.equals(clazz)) {
if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) {
return super.visit(node, data); return data;
}
clazz = clazz.getSuperclass();
}
}
return super.visit(node, data);
} }
@Override @Override
public Object visit(ASTMethodDeclaration node, Object data) { public Object visit(ASTMethodDeclaration node, Object data) {
ASTClassOrInterfaceDeclaration classOrInterface = node ASTClassOrInterfaceDeclaration classOrInterface = node
.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); .getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
if (classOrInterface != null && //Don't analyze enums, which cannot subclass clone() if (classOrInterface != null && //Don't analyze enums, which cannot subclass clone()
(node.isFinal() || classOrInterface.isFinal())) { (node.isFinal() || classOrInterface.isFinal())) {
if (node.findDescendantsOfType(ASTBlock.class).size() == 1) { if (node.findDescendantsOfType(ASTBlock.class).size() == 1) {
List<ASTBlockStatement> blocks = node.findDescendantsOfType(ASTBlockStatement.class); List<ASTBlockStatement> blocks = node.findDescendantsOfType(ASTBlockStatement.class);
if (blocks.size() == 1) { if (blocks.size() == 1) {
ASTBlockStatement block = blocks.get(0); ASTBlockStatement block = blocks.get(0);
ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class); ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
if (type != null && type.getType() != null && type.getNthParent(9).equals(node) if (type != null && type.getType() != null && type.getNthParent(9).equals(node)
&& type.getType().equals(CloneNotSupportedException.class)) { && type.getType().equals(CloneNotSupportedException.class)) {
return data; return data;
} else if (type != null && type.getType() == null } else if (type != null && type.getType() == null
&& "CloneNotSupportedException".equals(type.getImage())) { && "CloneNotSupportedException".equals(type.getImage())) {
return data; return data;
} }
} }
} }
} }
return super.visit(node, data); return super.visit(node, data);
} }
@Override @Override
public Object visit(ASTMethodDeclarator node, Object data) { public Object visit(ASTMethodDeclarator node, Object data) {
if (!"clone".equals(node.getImage())) { if (!"clone".equals(node.getImage())) {
return data; return data;
} }
int countParams = ((ASTFormalParameters) node.jjtGetChild(0)).jjtGetNumChildren(); int countParams = ((ASTFormalParameters) node.jjtGetChild(0)).jjtGetNumChildren();
if (countParams != 0) { if (countParams != 0) {
return data; return data;
} }
addViolation(data, node); addViolation(data, node);
return data; return data;
} }
} }