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
30 public class PrematureDeclarationRule extends AbstractJavaRule {
31
32
33
34
35
36
37
38
39
40
41
42 public Object visit(ASTLocalVariableDeclaration node, Object data) {
43
44
45 if (node.jjtGetParent() instanceof ASTForInit) {
46 return visit((AbstractJavaNode) node, data);
47
48 }
49
50 String varName = varNameIn(node);
51
52 AbstractJavaNode grandparent = (AbstractJavaNode) node.jjtGetParent().jjtGetParent();
53
54 List<ASTBlockStatement> nextBlocks = blocksAfter(grandparent, node);
55
56 for (ASTBlockStatement block : nextBlocks) {
57 if (hasReferencesIn(block, varName)) {
58 break;
59 }
60
61 if (hasExit(block)) {
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
80
81
82 public static boolean hasAsParentBetween(Node node, Class<?> intermediateParentClass, Node topParent) {
83
84 Node currentParent = node.jjtGetParent();
85
86 while (!currentParent.equals(topParent)) {
87 currentParent = currentParent.jjtGetParent();
88 if (currentParent.getClass().equals(intermediateParentClass)) {
89 return true;
90 }
91 }
92 return false;
93 }
94
95
96
97
98
99
100
101
102
103 @SuppressWarnings({ "rawtypes", "unchecked" })
104 private boolean hasExit(ASTBlockStatement block) {
105
106 List exitBlocks = block.findDescendantsOfType(ASTReturnStatement.class);
107 exitBlocks.addAll(block.findDescendantsOfType(ASTThrowStatement.class));
108
109 if (exitBlocks.isEmpty()) {
110 return false;
111 }
112
113
114
115 boolean result = false;
116 for (int i = 0; i < exitBlocks.size(); i++) {
117 Node exitNode = (Node) exitBlocks.get(i);
118 if (!hasAsParentBetween(exitNode, ASTMethodDeclaration.class, block)) {
119 result = true;
120 break;
121 }
122 }
123
124 return result;
125 }
126
127
128
129
130
131
132
133
134
135
136
137 private static boolean hasReferencesIn(ASTBlockStatement block, String varName) {
138
139 List<ASTName> names = block.findDescendantsOfType(ASTName.class);
140
141 for (ASTName name : names) {
142 if (isReference(varName, name.getImage())) {
143 return true;
144 }
145 }
146 return false;
147 }
148
149
150
151
152
153
154
155
156
157
158
159 private static boolean isReference(String shortName, String compoundName) {
160
161 int dotPos = compoundName.indexOf('.');
162
163 return dotPos < 0 ? shortName.equals(compoundName) : shortName.endsWith(compoundName.substring(0, dotPos));
164 }
165
166
167
168
169
170
171
172
173 private static String varNameIn(ASTLocalVariableDeclaration node) {
174 ASTVariableDeclarator declarator = node.getFirstChildOfType(ASTVariableDeclarator.class);
175 return ((ASTVariableDeclaratorId) declarator.jjtGetChild(0)).getImage();
176 }
177
178
179
180
181
182
183
184
185
186
187 private static int indexOf(AbstractJavaNode block, Node node) {
188
189 int count = block.jjtGetNumChildren();
190
191 for (int i = 0; i < count; i++) {
192 if (node == block.jjtGetChild(i)) {
193 return i;
194 }
195 }
196
197 return -1;
198 }
199
200
201
202
203
204
205
206
207
208
209
210 private static List<ASTBlockStatement> blocksAfter(AbstractJavaNode block, AbstractJavaNode node) {
211
212 int count = block.jjtGetNumChildren();
213 int start = indexOf(block, node.jjtGetParent()) + 1;
214
215 List<ASTBlockStatement> nextBlocks = new ArrayList<ASTBlockStatement>(count);
216
217 for (int i = start; i < count; i++) {
218 Node maybeBlock = block.jjtGetChild(i);
219 if (maybeBlock instanceof ASTBlockStatement) {
220 nextBlocks.add((ASTBlockStatement) maybeBlock);
221 }
222 }
223
224 return nextBlocks;
225 }
226 }