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