1
2
3
4 package net.sourceforge.pmd.lang.java.rule.optimizations;
5
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import net.sourceforge.pmd.lang.ast.Node;
10 import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
11 import net.sourceforge.pmd.lang.java.ast.ASTForInit;
12 import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
13 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
14 import net.sourceforge.pmd.lang.java.ast.ASTName;
15 import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
16 import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement;
17 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
18 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
19 import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
20 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
21
22
23
24
25
26
27
28
29 public class PrematureDeclarationRule extends AbstractJavaRule {
30
31 public PrematureDeclarationRule() { }
32
33
34
35
36
37
38
39
40 public Object visit(ASTLocalVariableDeclaration node, Object data) {
41
42
43 if (node.jjtGetParent().getClass().equals(ASTForInit.class)) {
44 return visit((AbstractJavaNode) node, data);
45 }
46
47 String varName = varNameIn(node);
48
49 AbstractJavaNode grandparent = (AbstractJavaNode)node.jjtGetParent().jjtGetParent();
50
51 List<Node> nextBlocks = blocksAfter(grandparent, node);
52
53 ASTBlockStatement statement;
54
55 for (Node block : nextBlocks) {
56
57 statement = (ASTBlockStatement)block;
58
59 if (hasReferencesIn(statement, varName)) break;
60
61 if (hasExit(statement)) {
62 addViolation(data, node, varName);
63 break;
64 }
65 }
66
67 return visit((AbstractJavaNode) node, data);
68 }
69
70
71
72
73
74
75
76
77
78
79 public static boolean hasAsParentBetween(Node node, Class<?> intermediateParentClass, Node topParent) {
80
81 Node currentParent = node.jjtGetParent();
82
83 while (currentParent != topParent) {
84 currentParent = currentParent.jjtGetParent();
85 if (currentParent.getClass().equals(intermediateParentClass)) return true;
86 }
87 return false;
88 }
89
90
91
92
93
94
95
96
97 @SuppressWarnings({ "rawtypes", "unchecked" })
98 private boolean hasExit(ASTBlockStatement block) {
99
100 List exitBlocks = block.findDescendantsOfType(ASTReturnStatement.class);
101 exitBlocks.addAll(block.findDescendantsOfType(ASTThrowStatement.class));
102
103 if (exitBlocks.isEmpty()) return false;
104
105
106 for (int i=0; i<exitBlocks.size(); i++) {
107 Node exitNode = (Node)exitBlocks.get(i);
108 if (hasAsParentBetween(exitNode, ASTMethodDeclaration.class, block)) continue;
109 return true;
110 }
111
112 return false;
113 }
114
115
116
117
118
119
120
121
122
123 private static boolean hasReferencesIn(ASTBlockStatement block, String varName) {
124
125 List<ASTName> names = block.findDescendantsOfType(ASTName.class);
126
127 for (ASTName name : names) {
128 if (isReference(varName, name.getImage())) return true;
129 }
130 return false;
131 }
132
133
134
135
136
137
138
139
140
141 private static boolean isReference(String shortName, String compoundName) {
142
143 int dotPos = compoundName.indexOf('.');
144
145 return dotPos < 0 ?
146 shortName.equals(compoundName) :
147 shortName.endsWith(compoundName.substring(0, dotPos));
148 }
149
150
151
152
153
154
155
156 private static String varNameIn(ASTLocalVariableDeclaration node) {
157 ASTVariableDeclarator declarator = node.getFirstChildOfType(ASTVariableDeclarator.class);
158 return ((ASTVariableDeclaratorId) declarator.jjtGetChild(0)).getImage();
159 }
160
161
162
163
164
165
166
167
168 private static int indexOf(AbstractJavaNode block, Node node) {
169
170 int count = block.jjtGetNumChildren();
171
172 for (int i=0; i<count; i++) {
173 if (node == block.jjtGetChild(i)) return i;
174 }
175
176 return -1;
177 }
178
179
180
181
182
183
184
185
186
187 private static List<Node> blocksAfter(AbstractJavaNode block, AbstractJavaNode node) {
188
189 int count = block.jjtGetNumChildren();
190 int start = indexOf(block, node.jjtGetParent()) + 1;
191
192 List<Node> nextBlocks = new ArrayList<Node>(count);
193
194 for (int i=start; i<count; i++) {
195 nextBlocks.add(block.jjtGetChild(i));
196 }
197
198 return nextBlocks;
199 }
200 }