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  				    String s = literal.getImage();
80  				    if (s.endsWith("l") || s.endsWith("L")) {
81  					s = s.substring(0, s.length() - 1);
82  				    }
83  				    value = Long.decode(s).doubleValue();
84  				} else if (literal.isFloatLiteral()) {
85  				    value = Double.parseDouble(literal.getImage());
86  				} else if (literal.isCharLiteral()) {
87  				    value = literal.getImage().charAt(1);
88  				}
89  
90  				if (value == 0) {
91  				    addViolation(data, variableDeclarator);
92  				}
93  			    }
94  			}
95  		    }
96  		}
97  	    }
98  	}
99  
100 	return data;
101     }
102 
103     /**
104      * Checks if a FieldDeclaration is a reference type (includes arrays). The reference information is in the
105      * FieldDeclaration for this example: <pre>int[] ia1</pre> and in the VariableDeclarator for this example:
106      * <pre>int ia2[];</pre>.
107      *
108      * @param fieldDeclaration the field to check.
109      * @param variableDeclarator the variable declarator to check.
110      * @return <code>true</code> if the field is a reference. <code>false</code> otherwise.
111      */
112     private boolean isRef(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator variableDeclarator) {
113 	Node type = fieldDeclaration.jjtGetChild(0).jjtGetChild(0);
114 	if (type instanceof ASTReferenceType) {
115 	    // Reference type, array or otherwise
116 	    return true;
117 	} else {
118 	    // Primitive array?
119 	    return ((ASTVariableDeclaratorId) variableDeclarator.jjtGetChild(0)).isArray();
120 	}
121     }
122 
123     private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
124 	super.addViolation(data, variableDeclarator, variableDeclarator.jjtGetChild(0).getImage());
125     }
126 }