001 package net.sourceforge.pmd.lang.java.rule.optimizations;
002
003 import net.sourceforge.pmd.lang.ast.Node;
004 import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
005 import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
006 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
007 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
008 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
009 import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
010 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
011 import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
012 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
013 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
014 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
015
016 /**
017 * Detects redundant field initializers, i.e. the field initializer expressions the JVM would assign by default.
018 *
019 * @author lucian.ciufudean@gmail.com
020 * @since Apr 10, 2009
021 */
022 public class RedundantFieldInitializerRule extends AbstractJavaRule {
023
024 public RedundantFieldInitializerRule() {
025 addRuleChainVisit(ASTFieldDeclaration.class);
026 }
027
028 public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
029 // Finals can only be initialized once.
030 if (fieldDeclaration.isFinal()) {
031 return data;
032 }
033
034 // Look for a match to the following XPath:
035 // VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal
036 for (ASTVariableDeclarator variableDeclarator : fieldDeclaration
037 .findChildrenOfType(ASTVariableDeclarator.class)) {
038 if (variableDeclarator.jjtGetNumChildren() > 1) {
039 final Node variableInitializer = variableDeclarator.jjtGetChild(1);
040 if (variableInitializer.jjtGetChild(0) instanceof ASTExpression) {
041 final Node expression = variableInitializer.jjtGetChild(0);
042 final Node primaryExpression;
043 if (expression.jjtGetNumChildren() == 1) {
044 if (expression.jjtGetChild(0) instanceof ASTPrimaryExpression) {
045 primaryExpression = expression.jjtGetChild(0);
046 } else if (expression.jjtGetChild(0) instanceof ASTCastExpression
047 && expression.jjtGetChild(0).jjtGetChild(1) instanceof ASTPrimaryExpression) {
048 primaryExpression = expression.jjtGetChild(0).jjtGetChild(1);
049 } else {
050 continue;
051 }
052 } else {
053 continue;
054 }
055 final Node primaryPrefix = primaryExpression.jjtGetChild(0);
056 if (primaryPrefix.jjtGetNumChildren() == 1 && primaryPrefix.jjtGetChild(0) instanceof ASTLiteral) {
057 final ASTLiteral literal = (ASTLiteral) primaryPrefix.jjtGetChild(0);
058 if (isRef(fieldDeclaration, variableDeclarator)) {
059 // Reference type
060 if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTNullLiteral) {
061 addViolation(data, variableDeclarator);
062 }
063 } else {
064 // Primitive type
065 if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTBooleanLiteral) {
066 // boolean type
067 ASTBooleanLiteral booleanLiteral = (ASTBooleanLiteral) literal.jjtGetChild(0);
068 if (!booleanLiteral.isTrue()) {
069 addViolation(data, variableDeclarator);
070 }
071 } else if (literal.jjtGetNumChildren() == 0) {
072 // numeric type
073 // Note: Not catching NumberFormatException, as it shouldn't be happening on valid source code.
074 double value = -1;
075 if (literal.isIntLiteral()) {
076 String s = literal.getImage();
077 if (s.endsWith("l") || s.endsWith("L")) {
078 s = s.substring(0, s.length() - 1);
079 }
080 value = Long.decode(s).doubleValue();
081 } else if (literal.isFloatLiteral()) {
082 value = Double.parseDouble(literal.getImage());
083 } else if (literal.isCharLiteral()) {
084 value = literal.getImage().charAt(1);
085 }
086
087 if (value == 0) {
088 addViolation(data, variableDeclarator);
089 }
090 }
091 }
092 }
093 }
094 }
095 }
096
097 return data;
098 }
099
100 /**
101 * Checks if a FieldDeclaration is a reference type (includes arrays). The reference information is in the
102 * FieldDeclaration for this example: <pre>int[] ia1</pre> and in the VariableDeclarator for this example:
103 * <pre>int ia2[];</pre>.
104 *
105 * @param fieldDeclaration the field to check.
106 * @param variableDeclarator the variable declarator to check.
107 * @return <code>true</code> if the field is a reference. <code>false</code> otherwise.
108 */
109 private boolean isRef(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator variableDeclarator) {
110 Node type = fieldDeclaration.jjtGetChild(0).jjtGetChild(0);
111 if (type instanceof ASTReferenceType) {
112 // Reference type, array or otherwise
113 return true;
114 } else {
115 // Primitive array?
116 return ((ASTVariableDeclaratorId) variableDeclarator.jjtGetChild(0)).isArray();
117 }
118 }
119
120 private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
121 super.addViolation(data, variableDeclarator, variableDeclarator.jjtGetChild(0).getImage());
122 }
123 }
|