1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle.checks.coding;
20
21 import com.puppycrawl.tools.checkstyle.api.Check;
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.FastStack;
24 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26 import java.util.HashMap;
27 import java.util.Map;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 public class FinalLocalVariableCheck extends Check
45 {
46
47
48
49
50
51 public static final String MSG_KEY = "final.variable";
52
53
54 private final FastStack<Map<String, DetailAST>> scopeStack =
55 FastStack.newInstance();
56
57 @Override
58 public int[] getDefaultTokens()
59 {
60 return new int[] {
61 TokenTypes.IDENT,
62 TokenTypes.CTOR_DEF,
63 TokenTypes.METHOD_DEF,
64 TokenTypes.VARIABLE_DEF,
65 TokenTypes.INSTANCE_INIT,
66 TokenTypes.STATIC_INIT,
67 TokenTypes.LITERAL_FOR,
68 TokenTypes.SLIST,
69 TokenTypes.OBJBLOCK,
70 };
71 }
72
73 @Override
74 public int[] getAcceptableTokens()
75 {
76 return new int[] {
77 TokenTypes.VARIABLE_DEF,
78 TokenTypes.PARAMETER_DEF,
79 };
80 }
81
82 @Override
83 public int[] getRequiredTokens()
84 {
85 return new int[] {
86 TokenTypes.IDENT,
87 TokenTypes.CTOR_DEF,
88 TokenTypes.METHOD_DEF,
89 TokenTypes.INSTANCE_INIT,
90 TokenTypes.STATIC_INIT,
91 TokenTypes.LITERAL_FOR,
92 TokenTypes.SLIST,
93 TokenTypes.OBJBLOCK,
94 };
95 }
96
97 @Override
98 public void visitToken(DetailAST ast)
99 {
100 switch (ast.getType()) {
101 case TokenTypes.OBJBLOCK:
102 case TokenTypes.SLIST:
103 case TokenTypes.LITERAL_FOR:
104 case TokenTypes.METHOD_DEF:
105 case TokenTypes.CTOR_DEF:
106 case TokenTypes.STATIC_INIT:
107 case TokenTypes.INSTANCE_INIT:
108 scopeStack.push(new HashMap<String, DetailAST>());
109 break;
110
111 case TokenTypes.PARAMETER_DEF:
112 if (ScopeUtils.inInterfaceBlock(ast)
113 || inAbstractOrNativeMethod(ast))
114 {
115 break;
116 }
117 case TokenTypes.VARIABLE_DEF:
118 if ((ast.getParent().getType() != TokenTypes.OBJBLOCK)
119 && (ast.getParent().getType() != TokenTypes.FOR_EACH_CLAUSE)
120 && isVariableInForInit(ast))
121 {
122 insertVariable(ast);
123 }
124 break;
125
126 case TokenTypes.IDENT:
127 final int parentType = ast.getParent().getType();
128 if ((TokenTypes.POST_DEC == parentType)
129 || (TokenTypes.DEC == parentType)
130 || (TokenTypes.POST_INC == parentType)
131 || (TokenTypes.INC == parentType)
132 || (TokenTypes.ASSIGN == parentType)
133 || (TokenTypes.PLUS_ASSIGN == parentType)
134 || (TokenTypes.MINUS_ASSIGN == parentType)
135 || (TokenTypes.DIV_ASSIGN == parentType)
136 || (TokenTypes.STAR_ASSIGN == parentType)
137 || (TokenTypes.MOD_ASSIGN == parentType)
138 || (TokenTypes.SR_ASSIGN == parentType)
139 || (TokenTypes.BSR_ASSIGN == parentType)
140 || (TokenTypes.SL_ASSIGN == parentType)
141 || (TokenTypes.BXOR_ASSIGN == parentType)
142 || (TokenTypes.BOR_ASSIGN == parentType)
143 || (TokenTypes.BAND_ASSIGN == parentType))
144 {
145
146
147 if (ast.getParent().getFirstChild() == ast) {
148 removeVariable(ast);
149 }
150 }
151 break;
152
153 default:
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169 private static boolean isVariableInForInit(DetailAST variableDef)
170 {
171 return variableDef.getParent().getType() != TokenTypes.FOR_INIT;
172 }
173
174
175
176
177
178
179 private static boolean inAbstractOrNativeMethod(DetailAST ast)
180 {
181 DetailAST parent = ast.getParent();
182 while (parent != null) {
183 if (parent.getType() == TokenTypes.METHOD_DEF) {
184 final DetailAST modifiers =
185 parent.findFirstToken(TokenTypes.MODIFIERS);
186 return modifiers.branchContains(TokenTypes.ABSTRACT)
187 || modifiers.branchContains(TokenTypes.LITERAL_NATIVE);
188 }
189 parent = parent.getParent();
190 }
191 return false;
192 }
193
194
195
196
197
198 private void insertVariable(DetailAST ast)
199 {
200 if (!ast.branchContains(TokenTypes.FINAL)) {
201 final Map<String, DetailAST> state = scopeStack.peek();
202 final DetailAST astNode = ast.findFirstToken(TokenTypes.IDENT);
203 state.put(astNode.getText(), astNode);
204 }
205 }
206
207
208
209
210
211 private void removeVariable(DetailAST ast)
212 {
213 for (int i = scopeStack.size() - 1; i >= 0; i--) {
214 final Map<String, DetailAST> state = scopeStack.peek(i);
215 final Object obj = state.remove(ast.getText());
216 if (obj != null) {
217 break;
218 }
219 }
220 }
221
222 @Override
223 public void leaveToken(DetailAST ast)
224 {
225 super.leaveToken(ast);
226
227 switch (ast.getType()) {
228 case TokenTypes.OBJBLOCK:
229 case TokenTypes.SLIST:
230 case TokenTypes.LITERAL_FOR:
231 case TokenTypes.CTOR_DEF:
232 case TokenTypes.STATIC_INIT:
233 case TokenTypes.INSTANCE_INIT:
234 case TokenTypes.METHOD_DEF:
235 final Map<String, DetailAST> state = scopeStack.pop();
236 for (DetailAST var : state.values()) {
237 log(var.getLineNo(), var.getColumnNo(), MSG_KEY, var
238 .getText());
239 }
240 break;
241
242 default:
243 }
244 }
245 }