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 }