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 reassignedVariableIdents.add(getNeededAssignIdent(assignToken));
166 }
167
168 }
169
170 return reassignedVariableIdents;
171
172 }
173
174
175
176
177
178
179
180 @Nullable
181 private static DetailAST getBranchLeadingToReassignedVars(DetailAST ast) {
182 DetailAST leadingToReassignedVarBranch = null;
183
184 for (DetailAST conditionalStatement = ast;
185 conditionalStatement != null && leadingToReassignedVarBranch == null;
186 conditionalStatement = conditionalStatement.getParent()) {
187
188 if (conditionalStatement.getType() == TokenTypes.LITERAL_IF
189 || conditionalStatement.getType() == TokenTypes.LITERAL_ELSE) {
190
191 leadingToReassignedVarBranch =
192 conditionalStatement.findFirstToken(TokenTypes.SLIST);
193
194 }
195 else if (conditionalStatement.getType() == TokenTypes.QUESTION) {
196 leadingToReassignedVarBranch = conditionalStatement;
197 }
198 }
199
200 return leadingToReassignedVarBranch;
201
202 }
203
204
205
206
207
208
209
210
211
212 @Nullable
213 private static DetailAST traverseUntilNeededBranchType(DetailAST startingBranch,
214 DetailAST bound, int neededTokenType) {
215
216 DetailAST match = null;
217
218 DetailAST iteratedBranch = shiftToNextTraversedBranch(startingBranch, bound);
219
220 while (iteratedBranch != null) {
221 if (iteratedBranch.getType() == neededTokenType) {
222 match = iteratedBranch;
223 break;
224 }
225
226 iteratedBranch = shiftToNextTraversedBranch(iteratedBranch, bound);
227 }
228
229 return match;
230 }
231
232
233
234
235
236
237
238
239 @Nullable
240 private static DetailAST shiftToNextTraversedBranch(DetailAST ast, DetailAST boundAst) {
241 DetailAST newAst = ast;
242
243 if (ast.getFirstChild() != null) {
244 newAst = ast.getFirstChild();
245 }
246 else {
247 while (newAst.getNextSibling() == null && !newAst.equals(boundAst)) {
248 newAst = newAst.getParent();
249 }
250 if (newAst.equals(boundAst)) {
251 newAst = null;
252 }
253 else {
254 newAst = newAst.getNextSibling();
255 }
256 }
257
258 return newAst;
259 }
260
261
262
263
264
265
266
267
268 @Nullable
269 private static DetailAST getMatchedAssignToken(DetailAST preAssignBranch) {
270 DetailAST matchedAssignToken = null;
271
272 for (int assignType : ASSIGN_TOKEN_TYPES) {
273 matchedAssignToken = preAssignBranch.findFirstToken(assignType);
274 if (matchedAssignToken != null) {
275 break;
276 }
277 }
278
279 return matchedAssignToken;
280 }
281
282
283
284
285
286
287
288 private static DetailAST getNeededAssignIdent(DetailAST assignToken) {
289 DetailAST assignIdent = assignToken;
290
291 while (traverseUntilNeededBranchType(
292 assignIdent, assignToken.getFirstChild(), TokenTypes.IDENT) != null) {
293
294 assignIdent =
295 traverseUntilNeededBranchType(assignIdent, assignToken, TokenTypes.IDENT);
296 }
297
298 return assignIdent;
299 }
300 }