1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.coding;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Set;
25
26 import javax.annotation.Nullable;
27
28 import com.puppycrawl.tools.checkstyle.StatelessCheck;
29 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30 import com.puppycrawl.tools.checkstyle.api.DetailAST;
31 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 @StatelessCheck
60 public class PatternVariableAssignmentCheck extends AbstractCheck {
61
62
63
64
65 public static final String MSG_KEY = "pattern.variable.assignment";
66
67
68
69
70 private static final Set<Integer> ASSIGN_TOKEN_TYPES = Set.of(
71 TokenTypes.ASSIGN, TokenTypes.PLUS_ASSIGN, TokenTypes.MINUS_ASSIGN, TokenTypes.STAR_ASSIGN,
72 TokenTypes.DIV_ASSIGN, TokenTypes.MOD_ASSIGN, TokenTypes.SR_ASSIGN, TokenTypes.BSR_ASSIGN,
73 TokenTypes.SL_ASSIGN, TokenTypes.BAND_ASSIGN, TokenTypes.BXOR_ASSIGN,
74 TokenTypes.BOR_ASSIGN);
75
76 @Override
77 public int[] getRequiredTokens() {
78 return new int[] {TokenTypes.LITERAL_INSTANCEOF};
79 }
80
81 @Override
82 public int[] getDefaultTokens() {
83 return getRequiredTokens();
84 }
85
86 @Override
87 public int[] getAcceptableTokens() {
88 return getRequiredTokens();
89 }
90
91 @Override
92 public void visitToken(DetailAST ast) {
93
94 final List<DetailAST> patternVariableIdents = getPatternVariableIdents(ast);
95 final List<DetailAST> reassignedVariableIdents = getReassignedVariableIdents(ast);
96
97 for (DetailAST patternVariableIdent : patternVariableIdents) {
98 for (DetailAST assignTokenIdent : reassignedVariableIdents) {
99 if (patternVariableIdent.getText().equals(assignTokenIdent.getText())) {
100
101 log(assignTokenIdent, MSG_KEY, assignTokenIdent.getText());
102 break;
103 }
104
105 }
106 }
107 }
108
109
110
111
112
113
114
115 private static List<DetailAST> getPatternVariableIdents(DetailAST ast) {
116
117 final DetailAST outermostPatternVariable =
118 ast.findFirstToken(TokenTypes.PATTERN_VARIABLE_DEF);
119
120 final DetailAST recordPatternDef;
121 if (ast.getType() == TokenTypes.LITERAL_INSTANCEOF) {
122 recordPatternDef = ast.findFirstToken(TokenTypes.RECORD_PATTERN_DEF);
123 }
124 else {
125 recordPatternDef = ast;
126 }
127
128 final List<DetailAST> patternVariableIdentsArray = new ArrayList<>();
129
130 if (outermostPatternVariable != null) {
131 patternVariableIdentsArray.add(
132 outermostPatternVariable.findFirstToken(TokenTypes.IDENT));
133 }
134 else if (recordPatternDef != null) {
135 final DetailAST recordPatternComponents = recordPatternDef
136 .findFirstToken(TokenTypes.RECORD_PATTERN_COMPONENTS);
137
138 if (recordPatternComponents != null) {
139 for (DetailAST innerPatternVariable = recordPatternComponents.getFirstChild();
140 innerPatternVariable != null;
141 innerPatternVariable = innerPatternVariable.getNextSibling()) {
142
143 if (innerPatternVariable.getType() == TokenTypes.PATTERN_VARIABLE_DEF) {
144 patternVariableIdentsArray.add(
145 innerPatternVariable.findFirstToken(TokenTypes.IDENT));
146 }
147 else {
148 patternVariableIdentsArray.addAll(
149 getPatternVariableIdents(innerPatternVariable));
150 }
151
152 }
153 }
154
155 }
156 return patternVariableIdentsArray;
157 }
158
159
160
161
162
163
164
165 private static List<DetailAST> getReassignedVariableIdents(DetailAST ast) {
166
167 final DetailAST branchLeadingToReassignedVar = getBranchLeadingToReassignedVars(ast);
168 final List<DetailAST> reassignedVariableIdents = new ArrayList<>();
169
170 for (DetailAST expressionBranch = branchLeadingToReassignedVar;
171 expressionBranch != null;
172 expressionBranch = traverseUntilNeededBranchType(expressionBranch,
173 branchLeadingToReassignedVar, TokenTypes.EXPR)) {
174
175 final DetailAST assignToken = getMatchedAssignToken(expressionBranch);
176
177 if (assignToken != null) {
178 reassignedVariableIdents.add(getNeededAssignIdent(assignToken));
179 }
180
181 }
182
183 return reassignedVariableIdents;
184
185 }
186
187
188
189
190
191
192
193 @Nullable
194 private static DetailAST getBranchLeadingToReassignedVars(DetailAST ast) {
195 DetailAST leadingToReassignedVarBranch = null;
196
197 for (DetailAST conditionalStatement = ast;
198 conditionalStatement != null && leadingToReassignedVarBranch == null;
199 conditionalStatement = conditionalStatement.getParent()) {
200
201 if (conditionalStatement.getType() == TokenTypes.LITERAL_IF
202 || conditionalStatement.getType() == TokenTypes.LITERAL_ELSE) {
203
204 leadingToReassignedVarBranch =
205 conditionalStatement.findFirstToken(TokenTypes.SLIST);
206
207 }
208 else if (conditionalStatement.getType() == TokenTypes.QUESTION) {
209 leadingToReassignedVarBranch = conditionalStatement;
210 }
211 }
212
213 return leadingToReassignedVarBranch;
214
215 }
216
217
218
219
220
221
222
223
224
225 @Nullable
226 private static DetailAST traverseUntilNeededBranchType(DetailAST startingBranch,
227 DetailAST bound, int neededTokenType) {
228
229 DetailAST match = null;
230
231 DetailAST iteratedBranch = shiftToNextTraversedBranch(startingBranch, bound);
232
233 while (iteratedBranch != null) {
234 if (iteratedBranch.getType() == neededTokenType) {
235 match = iteratedBranch;
236 break;
237 }
238
239 iteratedBranch = shiftToNextTraversedBranch(iteratedBranch, bound);
240 }
241
242 return match;
243 }
244
245
246
247
248
249
250
251
252 @Nullable
253 private static DetailAST shiftToNextTraversedBranch(DetailAST ast, DetailAST boundAst) {
254 DetailAST newAst = ast;
255
256 if (ast.getFirstChild() != null) {
257 newAst = ast.getFirstChild();
258 }
259 else {
260 while (newAst.getNextSibling() == null && !newAst.equals(boundAst)) {
261 newAst = newAst.getParent();
262 }
263 if (newAst.equals(boundAst)) {
264 newAst = null;
265 }
266 else {
267 newAst = newAst.getNextSibling();
268 }
269 }
270
271 return newAst;
272 }
273
274
275
276
277
278
279
280
281 @Nullable
282 private static DetailAST getMatchedAssignToken(DetailAST preAssignBranch) {
283 DetailAST matchedAssignToken = null;
284
285 for (int assignType : ASSIGN_TOKEN_TYPES) {
286 matchedAssignToken = preAssignBranch.findFirstToken(assignType);
287 if (matchedAssignToken != null) {
288 break;
289 }
290 }
291
292 return matchedAssignToken;
293 }
294
295
296
297
298
299
300
301 private static DetailAST getNeededAssignIdent(DetailAST assignToken) {
302 DetailAST assignIdent = assignToken;
303
304 while (traverseUntilNeededBranchType(
305 assignIdent, assignToken.getFirstChild(), TokenTypes.IDENT) != null) {
306
307 assignIdent =
308 traverseUntilNeededBranchType(assignIdent, assignToken, TokenTypes.IDENT);
309 }
310
311 return assignIdent;
312 }
313 }