Fixed bug 1031966

Re-Implemented CloneMethodMustImplementCloneable as a typeresolution rule. This rule can now detect super classes/interfaces which are cloneable.

The JUnit test was a bit tricky - I've added the interface as an actual concrete interface inside the xml package, and reference it from the xml test definitions. The JUnit works, this sort of design may be needed for other type resolution rules


git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/trunk@4875 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Allan Caplan
2006-12-14 01:33:39 +00:00
parent ca180e0ac2
commit db33e92828
6 changed files with 221 additions and 0 deletions

View File

@ -20,6 +20,7 @@ Fixed bug 1592710 - VariableNamingConventions no longer reports false positives
Fixed bug 1593292 - The CPD GUI now works with the 'by extension' option selected.
Fixed bug 1560944 - CPD now skips symlinks.
Fixed bug 1570824 - HTML reports generated on Windows no longer contain double backslashes. This caused problems when viewing those reports with Apache.
Fixed bug 1031966 - Re-Implemented CloneMethodMustImplementCloneable as a typeresolution rule. This rule can now detect super classes/interfaces which are cloneable
Applied patch 1551189 - SingularField false + for initialization blocks
Applied patch 1573981 - false + in CloneMethodMustImplementCloneable
Applied patch 1574988 - false + in OverrideBothEqualsAndHashcode

View File

@ -0,0 +1,20 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package test.net.sourceforge.pmd.rules.typeresolution.rules;
import net.sourceforge.pmd.Rule;
import test.net.sourceforge.pmd.testframework.SimpleAggregatorTst;
public class CloneMethodMustImplementCloneableTest extends SimpleAggregatorTst {
private Rule rule;
public void setUp() {
rule = findRule("typeresolution", "CloneMethodMustImplementCloneable");
}
public void testAll() {
runTests(rule);
}
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data>
<test-code>
<description><![CDATA[
ok, implements Cloneable
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo implements Cloneable {
void clone() {}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
bad, doesn't implement Cloneable
]]></description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {
void clone() {}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
ok, not Object.clone since method has a param
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
void clone(int x) {}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
ok, doesn't implement Cloneable but only throw CloneNotSupportedException
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
final Object clone() { throw new CloneNotSupportedException(); }
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
ok, inner class implements Cloneable
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Bar {
class Foo implements Cloneable {
void clone() {}
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
ok, implements interface which extends Cloneable
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class FooX extends test.net.sourceforge.pmd.rules.typeresolution.rules.xml.MyInterface {
void clone() {}
}
]]></code>
</test-code>
</test-data>

View File

@ -0,0 +1,5 @@
package test.net.sourceforge.pmd.rules.typeresolution.rules.xml;
public interface MyInterface extends Cloneable {
}

View File

@ -36,5 +36,30 @@ public class Bar {
</example>
</rule>
<rule name="CloneMethodMustImplementCloneable"
message="clone() method should be implemented only if implementing Cloneable interface"
class="net.sourceforge.pmd.typeresolution.rules.CloneMethodMustImplementCloneable"
typeResolution="true"
externalInfoUrl="http://pmd.sourceforge.net/rules/typeresolution.html#CloneMethodMustImplementCloneable">
<description>
The method clone() should only be implemented if the class implements the
Cloneable interface with the exception of a final method that only throws
CloneNotSupportedException. This version uses PMD's type resolution facilities,
and can detect if the class implements or extends a Cloneable class
</description>
<properties>
</properties>
<priority>3</priority>
<example>
<![CDATA[
public class MyClass {
public Object clone() throws CloneNotSupportedException {
return foo;
}
}
]]>
</example>
</rule>
</ruleset>

View File

@ -0,0 +1,98 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.rules;
import net.sourceforge.pmd.AbstractRule;
import net.sourceforge.pmd.ast.ASTBlock;
import net.sourceforge.pmd.ast.ASTBlockStatement;
import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.ast.ASTExtendsList;
import net.sourceforge.pmd.ast.ASTFormalParameters;
import net.sourceforge.pmd.ast.ASTImplementsList;
import net.sourceforge.pmd.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.ast.ASTMethodDeclarator;
import net.sourceforge.pmd.ast.SimpleNode;
import java.util.Arrays;
import java.util.List;
/**
* The method clone() should only be implemented if the class implements the
* Cloneable interface with the exception of a final method that only throws
* CloneNotSupportedException. This version uses PMD's type resolution
* facilities, and can detect if the class implements or extends a Cloneable
* class
*
* @author acaplan
*
*/
public class CloneMethodMustImplementCloneable extends AbstractRule {
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
ASTImplementsList impl = (ASTImplementsList) 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 {
List implementors = Arrays.asList(type.getType().getInterfaces());
if (implementors.contains(Cloneable.class)) {
return data;
}
}
}
}
if (node.jjtGetNumChildren() != 0 && node.jjtGetChild(0).getClass().equals(ASTExtendsList.class)) {
ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) ((SimpleNode) node.jjtGetChild(0)).jjtGetChild(0);
Class clazz = type.getType();
while (clazz != null && !clazz.equals(Object.class)) {
if (Arrays.asList(clazz.getInterfaces()).contains(Cloneable.class)) {
return data;
}
clazz = clazz.getSuperclass();
}
}
return super.visit(node, data);
}
public Object visit(ASTMethodDeclaration node, Object data) {
if (node.isFinal()) {
List blocks = node.findChildrenOfType(ASTBlock.class);
if (blocks.size() == 1) {
blocks = node.findChildrenOfType(ASTBlockStatement.class);
if (blocks.size() == 1) {
ASTBlockStatement block = (ASTBlockStatement) blocks.get(0);
ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) block
.getFirstChildOfType(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);
}
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;
}
}