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