[java] merge UnnecessaryFinalModifier into UnnecessaryModifier
- I take the chance to also flag final resources in try-with-resources - This resolves #411 - This resolves #676 - The rule is now using the rulechain
This commit is contained in:
@ -76,6 +76,7 @@ public class RuleSetFactoryCompatibility {
|
||||
addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports");
|
||||
addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException");
|
||||
addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName");
|
||||
addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier");
|
||||
}
|
||||
|
||||
void addFilterRuleRenamed(String language, String ruleset, String oldName, String newName) {
|
||||
|
@ -5,16 +5,31 @@
|
||||
package net.sourceforge.pmd.lang.java.rule.codestyle;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTResource;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
|
||||
public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
|
||||
public UnnecessaryModifierRule() {
|
||||
addRuleChainVisit(ASTEnumDeclaration.class);
|
||||
addRuleChainVisit(ASTAnnotationTypeDeclaration.class);
|
||||
addRuleChainVisit(ASTClassOrInterfaceDeclaration.class);
|
||||
addRuleChainVisit(ASTMethodDeclaration.class);
|
||||
addRuleChainVisit(ASTResource.class);
|
||||
addRuleChainVisit(ASTFieldDeclaration.class);
|
||||
addRuleChainVisit(ASTAnnotationMethodDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTEnumDeclaration node, Object data) {
|
||||
if (node.isStatic()) {
|
||||
@ -22,7 +37,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
addViolation(data, node, getMessage());
|
||||
}
|
||||
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
|
||||
@ -32,7 +47,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
}
|
||||
|
||||
if (!node.isNested()) {
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
Node parent = node.jjtGetParent().jjtGetParent().jjtGetParent();
|
||||
@ -49,7 +64,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
addViolation(data, node, getMessage());
|
||||
}
|
||||
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
|
||||
@ -59,7 +74,7 @@ public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
}
|
||||
|
||||
if (!node.isNested()) {
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
Node parent = node.jjtGetParent().jjtGetParent().jjtGetParent();
|
||||
@ -81,28 +96,67 @@ public class UnnecessaryModifierRule extends AbstractJavaRule {
|
||||
addViolation(data, node, getMessage());
|
||||
}
|
||||
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
public Object visit(final ASTMethodDeclaration node, Object data) {
|
||||
if (node.isSyntacticallyPublic() || node.isSyntacticallyAbstract()) {
|
||||
check(node, data);
|
||||
}
|
||||
return super.visit(node, data);
|
||||
|
||||
if (node.isFinal()) {
|
||||
// If the method is annotated by @SafeVarargs then it's ok
|
||||
if (!isSafeVarargs(node)) {
|
||||
if (node.isPrivate()) {
|
||||
addViolation(data, node);
|
||||
} else {
|
||||
final Node n = node.getNthParent(3);
|
||||
// A final method of an anonymous class / enum constant. Neither can be extended / overridden
|
||||
if (n instanceof ASTAllocationExpression || n instanceof ASTEnumConstant) {
|
||||
addViolation(data, node);
|
||||
} else if (n instanceof ASTClassOrInterfaceDeclaration
|
||||
&& ((ASTClassOrInterfaceDeclaration) n).isFinal()) {
|
||||
addViolation(data, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(final ASTResource node, final Object data) {
|
||||
if (node.isFinal()) {
|
||||
addViolation(data, node);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTFieldDeclaration node, Object data) {
|
||||
if (node.isSyntacticallyPublic() || node.isSyntacticallyStatic() || node.isSyntacticallyFinal()) {
|
||||
check(node, data);
|
||||
}
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
public Object visit(ASTAnnotationMethodDeclaration node, Object data) {
|
||||
if (node.isPublic() || node.isAbstract()) {
|
||||
check(node, data);
|
||||
}
|
||||
return super.visit(node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean isSafeVarargs(final ASTMethodDeclaration node) {
|
||||
for (final ASTAnnotation annotation : node.jjtGetParent().findChildrenOfType(ASTAnnotation.class)) {
|
||||
final Node childAnnotation = annotation.jjtGetChild(0);
|
||||
if (childAnnotation instanceof ASTMarkerAnnotation
|
||||
&& SafeVarargs.class.isAssignableFrom(((ASTMarkerAnnotation) childAnnotation).getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void check(Node fieldOrMethod, Object data) {
|
||||
|
@ -1435,45 +1435,6 @@ public class Foo {
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="UnnecessaryFinalModifier"
|
||||
language="java"
|
||||
since="3.0"
|
||||
message="Unnecessary final modifier in final class / private methods"
|
||||
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#unnecessaryfinalmodifier">
|
||||
<description>
|
||||
When a class has the final modifier, all the methods are automatically final and do not need to be
|
||||
tagged as such. Similarly, methods that can't be overridden (private methods, methods of anonymous classes,
|
||||
methods of enum instance) do not need to be tagged either.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ClassOrInterfaceDeclaration[@Final='true' and @Interface='false']
|
||||
/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration
|
||||
[count(./Annotation/MarkerAnnotation/Name[@Image='SafeVarargs' or @Image='java.lang.SafeVarargs']) = 0]
|
||||
/MethodDeclaration[@Final='true']
|
||||
| //MethodDeclaration[@Final='true' and @Private='true']
|
||||
| //EnumConstant/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration[@Final='true']
|
||||
| //AllocationExpression/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration[@Final='true']
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public final class Foo {
|
||||
// This final modifier is not necessary, since the class is final
|
||||
// and thus, all methods are final
|
||||
private final void foo() {
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="UnnecessaryFullyQualifiedName"
|
||||
language="java"
|
||||
since="5.0"
|
||||
|
@ -13,7 +13,6 @@ The Unnecessary Ruleset contains a collection of rules for unnecessary code.
|
||||
<rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals" deprecated="true" />
|
||||
<rule ref="category/java/errorprone.xml/UselessOperationOnImmutable" deprecated="true" />
|
||||
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryFinalModifier" deprecated="true" />
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryModifier" deprecated="true" />
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryReturn" deprecated="true" />
|
||||
<rule ref="category/java/codestyle.xml/UselessParentheses" deprecated="true" />
|
||||
|
@ -59,7 +59,6 @@ public class CodeStyleRulesTest extends SimpleAggregatorTst {
|
||||
addRule(RULESET, "SuspiciousConstantFieldName");
|
||||
addRule(RULESET, "TooManyStaticImports");
|
||||
addRule(RULESET, "UnnecessaryConstructor");
|
||||
addRule(RULESET, "UnnecessaryFinalModifier");
|
||||
addRule(RULESET, "UnnecessaryFullyQualifiedName");
|
||||
addRule(RULESET, "UnnecessaryLocalBeforeReturn");
|
||||
addRule(RULESET, "UnnecessaryModifier");
|
||||
|
@ -1,161 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<test-data
|
||||
xmlns="http://pmd.sourceforge.net/rule-tests"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST1
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public void foo() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST2
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public final void foo() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST3
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public final void foo() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST4
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void foo() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST5
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void foo() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
TEST6
|
||||
]]></description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void fooA() { }
|
||||
public final void fooS() { }
|
||||
public final void fooD() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method in inner class of non-final outer class
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public static class Bar {
|
||||
public final void buz() {}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method in inner final class
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final class Bar {
|
||||
public final void buz() {}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>#1464 UnnecessaryFinalModifier false positive on a @SafeVarargs method</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class InboxContents<T> {
|
||||
@SafeVarargs
|
||||
public final InboxContents<T> conflateWith(T... values) { // false positive
|
||||
return conflateWith(ImmutableList.copyOf(values));
|
||||
}
|
||||
}
|
||||
public final class InboxContents2 {
|
||||
@java.lang.SafeVarargs
|
||||
public final InboxContents conflateWith(String... values) {
|
||||
return conflateWith(ImmutableList.copyOf(values));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of private method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class TestClass {
|
||||
private final int getValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of enum method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public enum Foo {
|
||||
BAR {
|
||||
@Override
|
||||
public final void magic() {}
|
||||
};
|
||||
|
||||
public void magic() {}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of anonymous class method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public void stuff() {
|
||||
Listener list = new Listener() {
|
||||
@Override
|
||||
public final void onEvent() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
@ -428,6 +428,163 @@ public abstract interface TestInterface {
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public abstract @interface TestAnnotation {
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method on non-final class is ok
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public final void foo() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final and non-final methods mixed
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public final void foo() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method on a final class
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void foo() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
mixed final and non-final methods on final class
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void foo() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
multiple final methods on final class
|
||||
]]></description>
|
||||
<expected-problems>3</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final void fooA() { }
|
||||
public final void fooS() { }
|
||||
public final void fooD() { }
|
||||
public void foo2() { }
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method in inner class of non-final outer class
|
||||
]]></description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public static class Bar {
|
||||
public final void buz() {}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description><![CDATA[
|
||||
final method in inner final class
|
||||
]]></description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class Foo {
|
||||
public final class Bar {
|
||||
public final void buz() {}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
||||
<test-code>
|
||||
<description>#1464 UnnecessaryFinalModifier false positive on a @SafeVarargs method</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public final class InboxContents<T> {
|
||||
@SafeVarargs
|
||||
public final InboxContents<T> conflateWith(T... values) { // false positive
|
||||
return conflateWith(ImmutableList.copyOf(values));
|
||||
}
|
||||
}
|
||||
public final class InboxContents2 {
|
||||
@java.lang.SafeVarargs
|
||||
public final InboxContents conflateWith(String... values) {
|
||||
return conflateWith(ImmutableList.copyOf(values));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of private method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class TestClass {
|
||||
private final int getValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of enum method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public enum Foo {
|
||||
BAR {
|
||||
@Override
|
||||
public final void magic() {}
|
||||
};
|
||||
|
||||
public void magic() {}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of anonymous class method</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public void stuff() {
|
||||
Listener list = new Listener() {
|
||||
@Override
|
||||
public final void onEvent() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>Unnecessary final of try-with-resources resource</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Foo {
|
||||
public void stuff() {
|
||||
try (final FileWriter fw = new FileWriter(new File("something.txt"))) {
|
||||
// do something on fw
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
Reference in New Issue
Block a user