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