[java] RedundantFieldInitializer can not detect a special case for char initialize: char foo = '\0';

This commit is contained in:
Mykhailo Palahuta committed 2020-07-28 15:54:01 +03:00
1 parent 25405eb870
commit a707d94513
2 files changed
+88 -108

No files matched your search

@@ -4,17 +4,15 @@
package net.sourceforge.pmd.lang.java.rule.performance;
import net.sourceforge.pmd.lang.ast.Node;
import java.util.List;
import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
/**
@@ -32,115 +30,86 @@ public class RedundantFieldInitializerRule extends AbstractJavaRule {
@Override
public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
// Finals can only be initialized once.
if (fieldDeclaration.isFinal()) {
return data;
}
// Look for a match to the following XPath:
// VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal
for (ASTVariableDeclarator variableDeclarator : fieldDeclaration
.findChildrenOfType(ASTVariableDeclarator.class)) {
if (variableDeclarator.getNumChildren() > 1) {
final Node variableInitializer = variableDeclarator.getChild(1);
if (variableInitializer.getChild(0) instanceof ASTExpression) {
final Node expression = variableInitializer.getChild(0);
final Node primaryExpression;
if (expression.getNumChildren() == 1) {
if (expression.getChild(0) instanceof ASTPrimaryExpression) {
primaryExpression = expression.getChild(0);
} else if (expression.getChild(0) instanceof ASTCastExpression
&& expression.getChild(0).getChild(1) instanceof ASTPrimaryExpression) {
primaryExpression = expression.getChild(0).getChild(1);
} else {
continue;
}
} else {
continue;
}
final Node primaryPrefix = primaryExpression.getChild(0);
if (primaryPrefix.getNumChildren() == 1 && primaryPrefix.getChild(0) instanceof ASTLiteral) {
final ASTLiteral literal = (ASTLiteral) primaryPrefix.getChild(0);
if (isRef(fieldDeclaration, variableDeclarator)) {
// Reference type
if (literal.getNumChildren() == 1 && literal.getChild(0) instanceof ASTNullLiteral) {
addViolation(data, variableDeclarator);
}
} else {
// Primitive type
if (literal.getNumChildren() == 1
&& literal.getChild(0) instanceof ASTBooleanLiteral) {
// boolean type
ASTBooleanLiteral booleanLiteral = (ASTBooleanLiteral) literal.getChild(0);
if (!booleanLiteral.isTrue()) {
addViolation(data, variableDeclarator);
}
} else if (literal.getNumChildren() == 0) {
// numeric type
// Note: Not catching NumberFormatException, as
// it shouldn't be happening on valid source
// code.
Number value = -1;
if (literal.isIntLiteral()) {
value = literal.getValueAsInt();
} else if (literal.isLongLiteral()) {
value = literal.getValueAsLong();
} else if (literal.isFloatLiteral()) {
value = literal.getValueAsFloat();
} else if (literal.isDoubleLiteral()) {
value = literal.getValueAsDouble();
} else if (literal.isCharLiteral()) {
value = (int) literal.getImage().charAt(1);
}
if (value.doubleValue() == 0) {
addViolation(data, variableDeclarator);
}
}
}
}
if (declaresNotFinalField(fieldDeclaration)) {
List<ASTVariableDeclarator> varDecls = fieldDeclaration.findDescendantsOfType(ASTVariableDeclarator.class);
for (ASTVariableDeclarator varDecl : varDecls) {
if (hasRedundantInitializer(fieldDeclaration, varDecl)) {
addViolation(data, varDecl);
}
}
}
return data;
}
/**
* Checks if a FieldDeclaration is a reference type (includes arrays). The
* reference information is in the FieldDeclaration for this example:
*
* <pre>
* int[] ia1
* </pre>
*
* and in the VariableDeclarator for this example:
*
* <pre>
* int ia2[];
* </pre>
*
* .
*
* @param fieldDeclaration
* the field to check.
* @param variableDeclarator
* the variable declarator to check.
* @return <code>true</code> if the field is a reference. <code>false</code>
* otherwise.
*/
private boolean isRef(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator variableDeclarator) {
Node type = fieldDeclaration.getChild(0).getChild(0);
if (type instanceof ASTReferenceType) {
// Reference type, array or otherwise
return true;
} else {
// Primitive array?
return ((ASTVariableDeclaratorId) variableDeclarator.getChild(0)).isArray();
}
private boolean declaresNotFinalField(ASTFieldDeclaration fieldDeclaration) {
return !fieldDeclaration.isFinal();
}
private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
super.addViolation(data, variableDeclarator, variableDeclarator.getChild(0).getImage());
private boolean hasRedundantInitializer(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator varDecl) {
return declaresFieldOfPrimitiveType(fieldDeclaration)
&& hasRedundantInitializerOfPrimitive(varDecl)
|| hasRedundantInitializerOfReference(varDecl);
}
private boolean declaresFieldOfPrimitiveType(ASTFieldDeclaration fieldDeclaration) {
return fieldDeclaration.getChild(0).getChild(0) instanceof ASTPrimitiveType;
}
private boolean hasRedundantInitializerOfPrimitive(ASTVariableDeclarator varDecl) {
ASTLiteral literal = getLiteralValue(varDecl);
if (literal != null) {
if (isNumericLiteral(literal)) {
return getNumericLiteralValue(literal) == 0;
}
if (literal.isCharLiteral()) {
return hasDefaultCharLiteralValue(literal);
}
return isDefaultBooleanLiteral(literal);
}
return false;
}
private boolean isNumericLiteral(ASTLiteral literal) {
return literal.isIntLiteral() || literal.isLongLiteral()
|| literal.isFloatLiteral() || literal.isDoubleLiteral();
}
private double getNumericLiteralValue(ASTLiteral literal) {
if (literal.isIntLiteral()) {
return literal.getValueAsInt();
}
if (literal.isLongLiteral()) {
return literal.getValueAsLong();
}
if (literal.isFloatLiteral()) {
return literal.getValueAsFloat();
}
return literal.getValueAsDouble();
}
private boolean hasDefaultCharLiteralValue(ASTLiteral literal) {
String img = literal.getImage();
return img.contains("\u0000") || img.contains("\\0");
}
private boolean isDefaultBooleanLiteral(ASTLiteral literal) {
ASTBooleanLiteral booleanLiteral = literal.getFirstDescendantOfType(ASTBooleanLiteral.class);
return booleanLiteral != null && !booleanLiteral.isTrue();
}
private boolean hasRedundantInitializerOfReference(ASTVariableDeclarator varDecl) {
ASTLiteral literal = getLiteralValue(varDecl);
return literal != null && isNullLiteral(literal);
}
private ASTLiteral getLiteralValue(ASTVariableDeclarator varDecl) {
ASTPrimaryPrefix prefix = varDecl.getFirstDescendantOfType(ASTPrimaryPrefix.class);
return prefix != null && prefix.getNumChildren() == 1
? prefix.getFirstChildOfType(ASTLiteral.class)
: null;
}
private boolean isNullLiteral(ASTLiteral literal) {
return literal.getFirstDescendantOfType(ASTNullLiteral.class) != null;
}
}
@@ -1431,6 +1431,17 @@ public class Test {
public class RedundantFieldInitializerTest {
private long hexLongMinusOne = 0xffffffffffffffffL;
private int hexIntMinusOne = 0xffffffff;
}
]]></code>
</test-code>
<test-code>
<description>#2441 can not detect a case char c = '\0';</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>2</expected-linenumbers>
<code><![CDATA[
public class Foo {
char c = '\0';
}
]]></code>
</test-code>