View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.optimizations;
5   
6   import net.sourceforge.pmd.lang.ast.Node;
7   import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
8   import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
9   import net.sourceforge.pmd.lang.java.ast.ASTExpression;
10  import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
11  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
12  import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
13  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
14  import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
15  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
16  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
17  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
18  
19  /**
20   * Detects redundant field initializers, i.e. the field initializer expressions the JVM would assign by default.
21   *
22   * @author lucian.ciufudean@gmail.com
23   * @since Apr 10, 2009
24   */
25  public class RedundantFieldInitializerRule extends AbstractJavaRule {
26  
27      public RedundantFieldInitializerRule() {
28  	addRuleChainVisit(ASTFieldDeclaration.class);
29      }
30  
31      public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
32  	// Finals can only be initialized once.
33  	if (fieldDeclaration.isFinal()) {
34  	    return data;
35  	}
36  
37  	// Look for a match to the following XPath:
38  	//    VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal
39  	for (ASTVariableDeclarator variableDeclarator : fieldDeclaration
40  		.findChildrenOfType(ASTVariableDeclarator.class)) {
41  	    if (variableDeclarator.jjtGetNumChildren() > 1) {
42  		final Node variableInitializer = variableDeclarator.jjtGetChild(1);
43  		if (variableInitializer.jjtGetChild(0) instanceof ASTExpression) {
44  		    final Node expression = variableInitializer.jjtGetChild(0);
45  		    final Node primaryExpression;
46  		    if (expression.jjtGetNumChildren() == 1) {
47  			if (expression.jjtGetChild(0) instanceof ASTPrimaryExpression) {
48  			    primaryExpression = expression.jjtGetChild(0);
49  			} else if (expression.jjtGetChild(0) instanceof ASTCastExpression
50  				&& expression.jjtGetChild(0).jjtGetChild(1) instanceof ASTPrimaryExpression) {
51  			    primaryExpression = expression.jjtGetChild(0).jjtGetChild(1);
52  			} else {
53  			    continue;
54  			}
55  		    } else {
56  			continue;
57  		    }
58  		    final Node primaryPrefix = primaryExpression.jjtGetChild(0);
59  		    if (primaryPrefix.jjtGetNumChildren() == 1 && primaryPrefix.jjtGetChild(0) instanceof ASTLiteral) {
60  			final ASTLiteral literal = (ASTLiteral) primaryPrefix.jjtGetChild(0);
61  			if (isRef(fieldDeclaration, variableDeclarator)) {
62  			    // Reference type
63  			    if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTNullLiteral) {
64  				addViolation(data, variableDeclarator);
65  			    }
66  			} else {
67  			    // Primitive type
68  			    if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTBooleanLiteral) {
69  				// boolean type
70  				ASTBooleanLiteral booleanLiteral = (ASTBooleanLiteral) literal.jjtGetChild(0);
71  				if (!booleanLiteral.isTrue()) {
72  				    addViolation(data, variableDeclarator);
73  				}
74  			    } else if (literal.jjtGetNumChildren() == 0) {
75  				// numeric type
76  				// Note: Not catching NumberFormatException, as it shouldn't be happening on valid source code.
77  				double value = -1;
78  				if (literal.isIntLiteral()) {
79  				    value = Integer.decode(literal.getImage()).doubleValue();
80  				} else if (literal.isLongLiteral()) {
81  				    String s = literal.getImage();
82  				    // remove the ending "l" or "L" for long values
83  					s = s.substring(0, s.length() - 1);
84  				    value = Long.decode(s).doubleValue();
85  				} else if (literal.isFloatLiteral()) {
86                      String s = literal.getImage();
87                      // remove the ending "f" or "F" for float values
88                      s = s.substring(0, s.length() - 1);
89  				    value = Float.parseFloat(literal.getImage());
90  				} else if (literal.isDoubleLiteral()) {
91  				    value = Double.parseDouble(literal.getImage());
92  				} else if (literal.isCharLiteral()) {
93  				    value = literal.getImage().charAt(1);
94  				}
95  
96  				if (value == 0) {
97  				    addViolation(data, variableDeclarator);
98  				}
99  			    }
100 			}
101 		    }
102 		}
103 	    }
104 	}
105 
106 	return data;
107     }
108 
109     /**
110      * Checks if a FieldDeclaration is a reference type (includes arrays). The reference information is in the
111      * FieldDeclaration for this example: <pre>int[] ia1</pre> and in the VariableDeclarator for this example:
112      * <pre>int ia2[];</pre>.
113      *
114      * @param fieldDeclaration the field to check.
115      * @param variableDeclarator the variable declarator to check.
116      * @return <code>true</code> if the field is a reference. <code>false</code> otherwise.
117      */
118     private boolean isRef(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator variableDeclarator) {
119 	Node type = fieldDeclaration.jjtGetChild(0).jjtGetChild(0);
120 	if (type instanceof ASTReferenceType) {
121 	    // Reference type, array or otherwise
122 	    return true;
123 	} else {
124 	    // Primitive array?
125 	    return ((ASTVariableDeclaratorId) variableDeclarator.jjtGetChild(0)).isArray();
126 	}
127     }
128 
129     private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
130 	super.addViolation(data, variableDeclarator, variableDeclarator.jjtGetChild(0).getImage());
131     }
132 }