forked from phoedos/pmd
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:
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
@ -0,0 +1,5 @@
|
||||
package test.net.sourceforge.pmd.rules.typeresolution.rules.xml;
|
||||
|
||||
public interface MyInterface extends Cloneable {
|
||||
|
||||
}
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user