1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.ArrayDeque;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.Deque;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.LinkedList;
29  import java.util.Map;
30  import java.util.Queue;
31  import java.util.Set;
32  import java.util.stream.Collectors;
33  
34  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
35  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
36  import com.puppycrawl.tools.checkstyle.api.DetailAST;
37  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
38  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
39  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
40  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
41  
42  /**
43   * <p>
44   * Checks that references to instance variables and methods of the present
45   * object are explicitly of the form "this.varName" or "this.methodName(args)"
46   * and that those references don't rely on the default behavior when "this." is absent.
47   * </p>
48   * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false'
49   * and not that actual nowadays.</p>
50   * <p>Rationale:</p>
51   * <ol>
52   *   <li>
53   *     The same notation/habit for C++ and Java (C++ have global methods, so having
54   *     &quot;this.&quot; do make sense in it to distinguish call of method of class
55   *     instead of global).
56   *   </li>
57   *   <li>
58   *     Non-IDE development (ease of refactoring, some clearness to distinguish
59   *     static and non-static methods).
60   *   </li>
61   * </ol>
62   * <p>Limitations: Nothing is currently done about static variables
63   * or catch-blocks.  Static methods invoked on a class name seem to be OK;
64   * both the class name and the method name have a DOT parent.
65   * Non-static methods invoked on either this or a variable name seem to be
66   * OK, likewise.
67   * </p>
68   * <ul>
69   * <li>
70   * Property {@code checkFields} - Control whether to check references to fields.
71   * Default value is {@code true}.
72   * </li>
73   * <li>
74   * Property {@code checkMethods} - Control whether to check references to methods.
75   * Default value is {@code true}.
76   * </li>
77   * <li>
78   * Property {@code validateOnlyOverlapping} - Control whether to check only
79   * overlapping by variables or arguments.
80   * Default value is {@code true}.
81   * </li>
82   * </ul>
83   * <p>
84   * To configure the default check:
85   * </p>
86   * <pre>
87   * &lt;module name=&quot;RequireThis&quot;/&gt;
88   * </pre>
89   * <p>
90   * To configure to check the {@code this} qualifier for fields only:
91   * </p>
92   * <pre>
93   * &lt;module name=&quot;RequireThis&quot;&gt;
94   *   &lt;property name=&quot;checkMethods&quot; value=&quot;false&quot;/&gt;
95   * &lt;/module&gt;
96   * </pre>
97   * <p>
98   * Examples of how the check works if validateOnlyOverlapping option is set to true:
99   * </p>
100  * <pre>
101  * public static class A {
102  *   private int field1;
103  *   private int field2;
104  *
105  *   public A(int field1) {
106  *     // Overlapping by constructor argument.
107  *     field1 = field1; // violation: Reference to instance variable "field1" needs "this".
108  *     field2 = 0;
109  *   }
110  *
111  *   void foo3() {
112  *     String field1 = "values";
113  *     // Overlapping by local variable.
114  *     field1 = field1; // violation:  Reference to instance variable "field1" needs "this".
115  *   }
116  * }
117  *
118  * public static class B {
119  *   private int field;
120  *
121  *   public A(int f) {
122  *     field = f;
123  *   }
124  *
125  *   String addSuffixToField(String field) {
126  *     // Overlapping by method argument. Equal to "return field = field + "suffix";"
127  *     return field += "suffix"; // violation: Reference to instance variable "field" needs "this".
128  *   }
129  * }
130  * </pre>
131  * <p>
132  * Please, be aware of the following logic, which is implemented in the check:
133  * </p>
134  * <p>
135  * 1) If you arrange 'this' in your code on your own, the check will not raise violation for
136  * variables which use 'this' to reference a class field, for example:
137  * </p>
138  * <pre>
139  * public class C {
140  *   private int scale;
141  *   private int x;
142  *   public void foo(int scale) {
143  *     scale = this.scale; // no violation
144  *     if (scale &gt; 0) {
145  *       scale = -scale; // no violation
146  *     }
147  *     x *= scale;
148  *   }
149  * }
150  * </pre>
151  * <p>
152  * 2) If method parameter is returned from the method, the check will not raise violation for
153  * returned variable/parameter, for example:
154  * </p>
155  * <pre>
156  * public class D {
157  *   private String prefix;
158  *   public String modifyPrefix(String prefix) {
159  *     prefix = "^" + prefix + "$" // no violation (modification of parameter)
160  *     return prefix; // modified method parameter is returned from the method
161  *   }
162  * }
163  * </pre>
164  * <p>
165  * Examples of how the check works if validateOnlyOverlapping option is set to false:
166  * </p>
167  * <pre>
168  * public static class A {
169  *   private int field1;
170  *   private int field2;
171  *
172  *   public A(int field1) {
173  *     field1 = field1; // violation: Reference to instance variable "field1" needs "this".
174  *     field2 = 0; // violation: Reference to instance variable "field2" needs "this".
175  *     String field2;
176  *     field2 = "0"; // No violation. Local var allowed
177  *   }
178  *
179  *   void foo3() {
180  *     String field1 = "values";
181  *     field1 = field1; // violation:  Reference to instance variable "field1" needs "this".
182  *   }
183  * }
184  *
185  * public static class B {
186  *   private int field;
187  *
188  *   public A(int f) {
189  *     field = f; // violation:  Reference to instance variable "field" needs "this".
190  *   }
191  *
192  *   String addSuffixToField(String field) {
193  *     return field += "suffix"; // violation: Reference to instance variable "field" needs "this".
194  *   }
195  * }
196  *
197  * // If the variable is locally defined, there won't be a violation provided the variable
198  * // doesn't overlap.
199  * class C {
200  *   private String s1 = "foo1";
201  *   String s2 = "foo2";
202  *
203  *   C() {
204  *     s1 = "bar1"; // Violation. Reference to instance variable 's1' needs "this.".
205  *     String s2;
206  *     s2 = "bar2"; // No violation. Local var allowed.
207  *     s2 += s2; // Violation. Overlapping. Reference to instance variable 's2' needs "this.".
208  *   }
209  * }
210  * </pre>
211  *
212  * @since 3.4
213  */
214 // -@cs[ClassDataAbstractionCoupling] This check requires to work with and identify many frames.
215 @FileStatefulCheck
216 public class RequireThisCheck extends AbstractCheck {
217 
218     /**
219      * A key is pointing to the warning message text in "messages.properties"
220      * file.
221      */
222     public static final String MSG_METHOD = "require.this.method";
223     /**
224      * A key is pointing to the warning message text in "messages.properties"
225      * file.
226      */
227     public static final String MSG_VARIABLE = "require.this.variable";
228 
229     /** Set of all declaration tokens. */
230     private static final Set<Integer> DECLARATION_TOKENS = Collections.unmodifiableSet(
231         Arrays.stream(new Integer[] {
232             TokenTypes.VARIABLE_DEF,
233             TokenTypes.CTOR_DEF,
234             TokenTypes.METHOD_DEF,
235             TokenTypes.CLASS_DEF,
236             TokenTypes.ENUM_DEF,
237             TokenTypes.ANNOTATION_DEF,
238             TokenTypes.INTERFACE_DEF,
239             TokenTypes.PARAMETER_DEF,
240             TokenTypes.TYPE_ARGUMENT,
241         }).collect(Collectors.toSet()));
242     /** Set of all assign tokens. */
243     private static final Set<Integer> ASSIGN_TOKENS = Collections.unmodifiableSet(
244         Arrays.stream(new Integer[] {
245             TokenTypes.ASSIGN,
246             TokenTypes.PLUS_ASSIGN,
247             TokenTypes.STAR_ASSIGN,
248             TokenTypes.DIV_ASSIGN,
249             TokenTypes.MOD_ASSIGN,
250             TokenTypes.SR_ASSIGN,
251             TokenTypes.BSR_ASSIGN,
252             TokenTypes.SL_ASSIGN,
253             TokenTypes.BAND_ASSIGN,
254             TokenTypes.BXOR_ASSIGN,
255         }).collect(Collectors.toSet()));
256     /** Set of all compound assign tokens. */
257     private static final Set<Integer> COMPOUND_ASSIGN_TOKENS = Collections.unmodifiableSet(
258         Arrays.stream(new Integer[] {
259             TokenTypes.PLUS_ASSIGN,
260             TokenTypes.STAR_ASSIGN,
261             TokenTypes.DIV_ASSIGN,
262             TokenTypes.MOD_ASSIGN,
263             TokenTypes.SR_ASSIGN,
264             TokenTypes.BSR_ASSIGN,
265             TokenTypes.SL_ASSIGN,
266             TokenTypes.BAND_ASSIGN,
267             TokenTypes.BXOR_ASSIGN,
268         }).collect(Collectors.toSet()));
269 
270     /** Frame for the currently processed AST. */
271     private final Deque<AbstractFrame> current = new ArrayDeque<>();
272 
273     /** Tree of all the parsed frames. */
274     private Map<DetailAST, AbstractFrame> frames;
275 
276     /** Control whether to check references to fields. */
277     private boolean checkFields = true;
278     /** Control whether to check references to methods. */
279     private boolean checkMethods = true;
280     /** Control whether to check only overlapping by variables or arguments. */
281     private boolean validateOnlyOverlapping = true;
282 
283     /**
284      * Setter to control whether to check references to fields.
285      * @param checkFields should we check fields usage or not.
286      */
287     public void setCheckFields(boolean checkFields) {
288         this.checkFields = checkFields;
289     }
290 
291     /**
292      * Setter to control whether to check references to methods.
293      * @param checkMethods should we check methods usage or not.
294      */
295     public void setCheckMethods(boolean checkMethods) {
296         this.checkMethods = checkMethods;
297     }
298 
299     /**
300      * Setter to control whether to check only overlapping by variables or arguments.
301      * @param validateOnlyOverlapping should we check only overlapping by variables or arguments.
302      */
303     public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) {
304         this.validateOnlyOverlapping = validateOnlyOverlapping;
305     }
306 
307     @Override
308     public int[] getDefaultTokens() {
309         return getRequiredTokens();
310     }
311 
312     @Override
313     public int[] getRequiredTokens() {
314         return new int[] {
315             TokenTypes.CLASS_DEF,
316             TokenTypes.INTERFACE_DEF,
317             TokenTypes.ENUM_DEF,
318             TokenTypes.ANNOTATION_DEF,
319             TokenTypes.CTOR_DEF,
320             TokenTypes.METHOD_DEF,
321             TokenTypes.LITERAL_FOR,
322             TokenTypes.SLIST,
323             TokenTypes.IDENT,
324         };
325     }
326 
327     @Override
328     public int[] getAcceptableTokens() {
329         return getRequiredTokens();
330     }
331 
332     @Override
333     public void beginTree(DetailAST rootAST) {
334         frames = new HashMap<>();
335         current.clear();
336 
337         final Deque<AbstractFrame> frameStack = new LinkedList<>();
338         DetailAST curNode = rootAST;
339         while (curNode != null) {
340             collectDeclarations(frameStack, curNode);
341             DetailAST toVisit = curNode.getFirstChild();
342             while (curNode != null && toVisit == null) {
343                 endCollectingDeclarations(frameStack, curNode);
344                 toVisit = curNode.getNextSibling();
345                 if (toVisit == null) {
346                     curNode = curNode.getParent();
347                 }
348             }
349             curNode = toVisit;
350         }
351     }
352 
353     @Override
354     public void visitToken(DetailAST ast) {
355         switch (ast.getType()) {
356             case TokenTypes.IDENT :
357                 processIdent(ast);
358                 break;
359             case TokenTypes.CLASS_DEF :
360             case TokenTypes.INTERFACE_DEF :
361             case TokenTypes.ENUM_DEF :
362             case TokenTypes.ANNOTATION_DEF :
363             case TokenTypes.SLIST :
364             case TokenTypes.METHOD_DEF :
365             case TokenTypes.CTOR_DEF :
366             case TokenTypes.LITERAL_FOR :
367                 current.push(frames.get(ast));
368                 break;
369             default :
370                 // do nothing
371         }
372     }
373 
374     @Override
375     public void leaveToken(DetailAST ast) {
376         switch (ast.getType()) {
377             case TokenTypes.CLASS_DEF :
378             case TokenTypes.INTERFACE_DEF :
379             case TokenTypes.ENUM_DEF :
380             case TokenTypes.ANNOTATION_DEF :
381             case TokenTypes.SLIST :
382             case TokenTypes.METHOD_DEF :
383             case TokenTypes.CTOR_DEF :
384             case TokenTypes.LITERAL_FOR:
385                 current.pop();
386                 break;
387             default :
388                 // do nothing
389         }
390     }
391 
392     /**
393      * Checks if a given IDENT is method call or field name which
394      * requires explicit {@code this} qualifier.
395      * @param ast IDENT to check.
396      */
397     private void processIdent(DetailAST ast) {
398         int parentType = ast.getParent().getType();
399         if (parentType == TokenTypes.EXPR
400                 && ast.getParent().getParent().getParent().getType()
401                     == TokenTypes.ANNOTATION_FIELD_DEF) {
402             parentType = TokenTypes.ANNOTATION_FIELD_DEF;
403         }
404         switch (parentType) {
405             case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
406             case TokenTypes.ANNOTATION:
407             case TokenTypes.ANNOTATION_FIELD_DEF:
408                 // no need to check annotations content
409                 break;
410             case TokenTypes.METHOD_CALL:
411                 if (checkMethods) {
412                     final AbstractFrame frame = getMethodWithoutThis(ast);
413                     if (frame != null) {
414                         logViolation(MSG_METHOD, ast, frame);
415                     }
416                 }
417                 break;
418             default:
419                 if (checkFields) {
420                     final AbstractFrame frame = getFieldWithoutThis(ast, parentType);
421                     if (frame != null) {
422                         logViolation(MSG_VARIABLE, ast, frame);
423                     }
424                 }
425                 break;
426         }
427     }
428 
429     /**
430      * Helper method to log a LocalizedMessage.
431      * @param ast a node to get line id column numbers associated with the message.
432      * @param msgKey key to locale message format.
433      * @param frame the class frame where the violation is found.
434      */
435     private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
436         if (frame.getFrameName().equals(getNearestClassFrameName())) {
437             log(ast, msgKey, ast.getText(), "");
438         }
439         else if (!(frame instanceof AnonymousClassFrame)) {
440             log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
441         }
442     }
443 
444     /**
445      * Returns the frame where the field is declared, if the given field is used without
446      * 'this', and null otherwise.
447      * @param ast field definition ast token.
448      * @param parentType type of the parent.
449      * @return the frame where the field is declared, if the given field is used without
450      *         'this' and null otherwise.
451      */
452     private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) {
453         final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast) == null;
454         final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
455                 && ast.getPreviousSibling() != null;
456         final boolean typeName = parentType == TokenTypes.TYPE
457                 || parentType == TokenTypes.LITERAL_NEW;
458         AbstractFrame frame = null;
459 
460         if (!importOrPackage
461                 && !methodNameInMethodCall
462                 && !typeName
463                 && !isDeclarationToken(parentType)
464                 && !isLambdaParameter(ast)) {
465             final AbstractFrame fieldFrame = findClassFrame(ast, false);
466 
467             if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) {
468                 frame = getClassFrameWhereViolationIsFound(ast);
469             }
470         }
471         return frame;
472     }
473 
474     /**
475      * Parses the next AST for declarations.
476      * @param frameStack stack containing the FrameTree being built.
477      * @param ast AST to parse.
478      */
479     // -@cs[JavaNCSS] This method is a big switch and is too hard to remove.
480     private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) {
481         final AbstractFrame frame = frameStack.peek();
482         switch (ast.getType()) {
483             case TokenTypes.VARIABLE_DEF :
484                 collectVariableDeclarations(ast, frame);
485                 break;
486             case TokenTypes.PARAMETER_DEF :
487                 if (!CheckUtil.isReceiverParameter(ast)
488                         && !isLambdaParameter(ast)
489                         && ast.getParent().getType() != TokenTypes.LITERAL_CATCH) {
490                     final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
491                     frame.addIdent(parameterIdent);
492                 }
493                 break;
494             case TokenTypes.CLASS_DEF :
495             case TokenTypes.INTERFACE_DEF :
496             case TokenTypes.ENUM_DEF :
497             case TokenTypes.ANNOTATION_DEF :
498                 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
499                 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent));
500                 break;
501             case TokenTypes.SLIST :
502                 frameStack.addFirst(new BlockFrame(frame, ast));
503                 break;
504             case TokenTypes.METHOD_DEF :
505                 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
506                 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
507                 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
508                     ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent);
509                 }
510                 else {
511                     ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent);
512                 }
513                 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent));
514                 break;
515             case TokenTypes.CTOR_DEF :
516                 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT);
517                 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent));
518                 break;
519             case TokenTypes.ENUM_CONSTANT_DEF :
520                 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
521                 ((ClassFrame) frame).addStaticMember(ident);
522                 break;
523             case TokenTypes.LITERAL_CATCH:
524                 final AbstractFrame catchFrame = new CatchFrame(frame, ast);
525                 catchFrame.addIdent(ast.findFirstToken(TokenTypes.PARAMETER_DEF).findFirstToken(
526                         TokenTypes.IDENT));
527                 frameStack.addFirst(catchFrame);
528                 break;
529             case TokenTypes.LITERAL_FOR:
530                 final AbstractFrame forFrame = new ForFrame(frame, ast);
531                 frameStack.addFirst(forFrame);
532                 break;
533             case TokenTypes.LITERAL_NEW:
534                 if (isAnonymousClassDef(ast)) {
535                     frameStack.addFirst(new AnonymousClassFrame(frame,
536                             ast.getFirstChild().toString()));
537                 }
538                 break;
539             default:
540                 // do nothing
541         }
542     }
543 
544     /**
545      * Collects variable declarations.
546      * @param ast variable token.
547      * @param frame current frame.
548      */
549     private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
550         final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
551         if (frame.getType() == FrameType.CLASS_FRAME) {
552             final DetailAST mods =
553                     ast.findFirstToken(TokenTypes.MODIFIERS);
554             if (ScopeUtil.isInInterfaceBlock(ast)
555                     || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
556                 ((ClassFrame) frame).addStaticMember(ident);
557             }
558             else {
559                 ((ClassFrame) frame).addInstanceMember(ident);
560             }
561         }
562         else {
563             frame.addIdent(ident);
564         }
565     }
566 
567     /**
568      * Ends parsing of the AST for declarations.
569      * @param frameStack Stack containing the FrameTree being built.
570      * @param ast AST that was parsed.
571      */
572     private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) {
573         switch (ast.getType()) {
574             case TokenTypes.CLASS_DEF :
575             case TokenTypes.INTERFACE_DEF :
576             case TokenTypes.ENUM_DEF :
577             case TokenTypes.ANNOTATION_DEF :
578             case TokenTypes.SLIST :
579             case TokenTypes.METHOD_DEF :
580             case TokenTypes.CTOR_DEF :
581             case TokenTypes.LITERAL_CATCH :
582             case TokenTypes.LITERAL_FOR :
583                 frames.put(ast, frameStack.poll());
584                 break;
585             case TokenTypes.LITERAL_NEW :
586                 if (isAnonymousClassDef(ast)) {
587                     frames.put(ast, frameStack.poll());
588                 }
589                 break;
590             default :
591                 // do nothing
592         }
593     }
594 
595     /**
596      * Whether the AST is a definition of an anonymous class.
597      * @param ast the AST to process.
598      * @return true if the AST is a definition of an anonymous class.
599      */
600     private static boolean isAnonymousClassDef(DetailAST ast) {
601         final DetailAST lastChild = ast.getLastChild();
602         return lastChild != null
603             && lastChild.getType() == TokenTypes.OBJBLOCK;
604     }
605 
606     /**
607      * Returns the class frame where violation is found (where the field is used without 'this')
608      * or null otherwise.
609      * @param ast IDENT ast to check.
610      * @return the class frame where violation is found or null otherwise.
611      */
612     // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain
613     // a logic, additional abstraction will not make logic/algorithm more readable.
614     private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) {
615         AbstractFrame frameWhereViolationIsFound = null;
616         final AbstractFrame variableDeclarationFrame = findFrame(ast, false);
617         final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType();
618         final DetailAST prevSibling = ast.getPreviousSibling();
619         if (variableDeclarationFrameType == FrameType.CLASS_FRAME
620                 && !validateOnlyOverlapping
621                 && prevSibling == null
622                 && canBeReferencedFromStaticContext(ast)) {
623             frameWhereViolationIsFound = variableDeclarationFrame;
624         }
625         else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) {
626             if (isOverlappingByArgument(ast)) {
627                 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
628                         && !isReturnedVariable(variableDeclarationFrame, ast)
629                         && canBeReferencedFromStaticContext(ast)
630                         && canAssignValueToClassField(ast)) {
631                     frameWhereViolationIsFound = findFrame(ast, true);
632                 }
633             }
634             else if (!validateOnlyOverlapping
635                      && prevSibling == null
636                      && isAssignToken(ast.getParent().getType())
637                      && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
638                      && canBeReferencedFromStaticContext(ast)
639                      && canAssignValueToClassField(ast)) {
640                 frameWhereViolationIsFound = findFrame(ast, true);
641             }
642         }
643         else if (variableDeclarationFrameType == FrameType.CTOR_FRAME
644                  && isOverlappingByArgument(ast)
645                  && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) {
646             frameWhereViolationIsFound = findFrame(ast, true);
647         }
648         else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME
649                     && isOverlappingByLocalVariable(ast)
650                     && canAssignValueToClassField(ast)
651                     && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)
652                     && !isReturnedVariable(variableDeclarationFrame, ast)
653                     && canBeReferencedFromStaticContext(ast)) {
654             frameWhereViolationIsFound = findFrame(ast, true);
655         }
656         return frameWhereViolationIsFound;
657     }
658 
659     /**
660      * Checks whether user arranges 'this' for variable in method, constructor, or block on his own.
661      * @param currentFrame current frame.
662      * @param ident ident token.
663      * @return true if user arranges 'this' for variable in method, constructor,
664      *         or block on his own.
665      */
666     private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame,
667                                                           DetailAST ident) {
668         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
669         final DetailAST definitionToken = blockFrameNameIdent.getParent();
670         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
671         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
672 
673         boolean userDefinedArrangementOfThis = false;
674 
675         final Set<DetailAST> variableUsagesInsideBlock =
676             getAllTokensWhichAreEqualToCurrent(definitionToken, ident,
677                 blockEndToken.getLineNo());
678 
679         for (DetailAST variableUsage : variableUsagesInsideBlock) {
680             final DetailAST prevSibling = variableUsage.getPreviousSibling();
681             if (prevSibling != null
682                     && prevSibling.getType() == TokenTypes.LITERAL_THIS) {
683                 userDefinedArrangementOfThis = true;
684                 break;
685             }
686         }
687         return userDefinedArrangementOfThis;
688     }
689 
690     /**
691      * Returns the token which ends the code block.
692      * @param blockNameIdent block name identifier.
693      * @param blockStartToken token which starts the block.
694      * @return the token which ends the code block.
695      */
696     private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) {
697         DetailAST blockEndToken = null;
698         final DetailAST blockNameIdentParent = blockNameIdent.getParent();
699         if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) {
700             blockEndToken = blockNameIdentParent.getNextSibling();
701         }
702         else {
703             final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent,
704                     TokenTypes.RCURLY);
705             for (DetailAST currentRcurly : rcurlyTokens) {
706                 final DetailAST parent = currentRcurly.getParent();
707                 if (blockStartToken.getLineNo() == parent.getLineNo()) {
708                     blockEndToken = currentRcurly;
709                 }
710             }
711         }
712         return blockEndToken;
713     }
714 
715     /**
716      * Checks whether the current variable is returned from the method.
717      * @param currentFrame current frame.
718      * @param ident variable ident token.
719      * @return true if the current variable is returned from the method.
720      */
721     private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) {
722         final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent();
723         final DetailAST definitionToken = blockFrameNameIdent.getParent();
724         final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST);
725         final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken);
726 
727         final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken,
728             TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo());
729 
730         boolean returnedVariable = false;
731         for (DetailAST returnToken : returnsInsideBlock) {
732             returnedVariable = isAstInside(returnToken, ident);
733             if (returnedVariable) {
734                 break;
735             }
736         }
737         return returnedVariable;
738     }
739 
740     /**
741      * Checks if the given {@code ast} is equal to the {@code tree} or a child of it.
742      * @param tree The tree to search.
743      * @param ast The AST to look for.
744      * @return {@code true} if the {@code ast} was found.
745      */
746     private static boolean isAstInside(DetailAST tree, DetailAST ast) {
747         boolean result = false;
748 
749         if (tree.equals(ast)) {
750             result = true;
751         }
752         else {
753             for (DetailAST child = tree.getFirstChild(); child != null
754                     && !result; child = child.getNextSibling()) {
755                 result = isAstInside(child, ast);
756             }
757         }
758 
759         return result;
760     }
761 
762     /**
763      * Checks whether a field can be referenced from a static context.
764      * @param ident ident token.
765      * @return true if field can be referenced from a static context.
766      */
767     private boolean canBeReferencedFromStaticContext(DetailAST ident) {
768         AbstractFrame variableDeclarationFrame = findFrame(ident, false);
769         boolean staticInitializationBlock = false;
770         while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME
771                 || variableDeclarationFrame.getType() == FrameType.FOR_FRAME) {
772             final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent();
773             final DetailAST definitionToken = blockFrameNameIdent.getParent();
774             if (definitionToken.getType() == TokenTypes.STATIC_INIT) {
775                 staticInitializationBlock = true;
776                 break;
777             }
778             variableDeclarationFrame = variableDeclarationFrame.getParent();
779         }
780 
781         boolean staticContext = false;
782         if (staticInitializationBlock) {
783             staticContext = true;
784         }
785         else {
786             if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) {
787                 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident);
788                 if (codeBlockDefinition != null) {
789                     final DetailAST modifiers = codeBlockDefinition.getFirstChild();
790                     staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT
791                         || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
792                 }
793             }
794             else {
795                 final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent();
796                 final DetailAST definitionToken = frameNameIdent.getParent();
797                 staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS)
798                         .findFirstToken(TokenTypes.LITERAL_STATIC) != null;
799             }
800         }
801         return !staticContext;
802     }
803 
804     /**
805      * Returns code block definition token for current identifier.
806      * @param ident ident token.
807      * @return code block definition token for current identifier or null if code block
808      *         definition was not found.
809      */
810     private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) {
811         DetailAST parent = ident.getParent();
812         while (parent != null
813                && parent.getType() != TokenTypes.METHOD_DEF
814                && parent.getType() != TokenTypes.CTOR_DEF
815                && parent.getType() != TokenTypes.STATIC_INIT) {
816             parent = parent.getParent();
817         }
818         return parent;
819     }
820 
821     /**
822      * Checks whether a value can be assigned to a field.
823      * A value can be assigned to a final field only in constructor block. If there is a method
824      * block, value assignment can be performed only to non final field.
825      * @param ast an identifier token.
826      * @return true if a value can be assigned to a field.
827      */
828     private boolean canAssignValueToClassField(DetailAST ast) {
829         final AbstractFrame fieldUsageFrame = findFrame(ast, false);
830         final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame);
831 
832         final AbstractFrame declarationFrame = findFrame(ast, true);
833         final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast);
834 
835         return fieldUsageInConstructor || !finalField;
836     }
837 
838     /**
839      * Checks whether a field usage frame is inside constructor frame.
840      * @param frame frame, where field is used.
841      * @return true if the field usage frame is inside constructor frame.
842      */
843     private static boolean isInsideConstructorFrame(AbstractFrame frame) {
844         boolean assignmentInConstructor = false;
845         AbstractFrame fieldUsageFrame = frame;
846         if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
847             while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) {
848                 fieldUsageFrame = fieldUsageFrame.getParent();
849             }
850             if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) {
851                 assignmentInConstructor = true;
852             }
853         }
854         return assignmentInConstructor;
855     }
856 
857     /**
858      * Checks whether an overlapping by method or constructor argument takes place.
859      * @param ast an identifier.
860      * @return true if an overlapping by method or constructor argument takes place.
861      */
862     private boolean isOverlappingByArgument(DetailAST ast) {
863         boolean overlapping = false;
864         final DetailAST parent = ast.getParent();
865         final DetailAST sibling = ast.getNextSibling();
866         if (sibling != null && isAssignToken(parent.getType())) {
867             if (isCompoundAssignToken(parent.getType())) {
868                 overlapping = true;
869             }
870             else {
871                 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
872                 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
873                 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
874             }
875         }
876         return overlapping;
877     }
878 
879     /**
880      * Checks whether an overlapping by local variable takes place.
881      * @param ast an identifier.
882      * @return true if an overlapping by local variable takes place.
883      */
884     private boolean isOverlappingByLocalVariable(DetailAST ast) {
885         boolean overlapping = false;
886         final DetailAST parent = ast.getParent();
887         final DetailAST sibling = ast.getNextSibling();
888         if (sibling != null && isAssignToken(parent.getType())) {
889             final ClassFrame classFrame = (ClassFrame) findFrame(ast, true);
890             final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT);
891             overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast);
892         }
893         return overlapping;
894     }
895 
896     /**
897      * Collects all tokens of specific type starting with the current ast node.
898      * @param ast ast node.
899      * @param tokenType token type.
900      * @return a set of all tokens of specific type starting with the current ast node.
901      */
902     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) {
903         DetailAST vertex = ast;
904         final Set<DetailAST> result = new HashSet<>();
905         final Deque<DetailAST> stack = new ArrayDeque<>();
906         while (vertex != null || !stack.isEmpty()) {
907             if (!stack.isEmpty()) {
908                 vertex = stack.pop();
909             }
910             while (vertex != null) {
911                 if (vertex.getType() == tokenType) {
912                     result.add(vertex);
913                 }
914                 if (vertex.getNextSibling() != null) {
915                     stack.push(vertex.getNextSibling());
916                 }
917                 vertex = vertex.getFirstChild();
918             }
919         }
920         return result;
921     }
922 
923     /**
924      * Collects all tokens of specific type starting with the current ast node and which line
925      * number is lower or equal to the end line number.
926      * @param ast ast node.
927      * @param tokenType token type.
928      * @param endLineNumber end line number.
929      * @return a set of all tokens of specific type starting with the current ast node and which
930      *         line number is lower or equal to the end line number.
931      */
932     private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType,
933                                                      int endLineNumber) {
934         DetailAST vertex = ast;
935         final Set<DetailAST> result = new HashSet<>();
936         final Deque<DetailAST> stack = new ArrayDeque<>();
937         while (vertex != null || !stack.isEmpty()) {
938             if (!stack.isEmpty()) {
939                 vertex = stack.pop();
940             }
941             while (vertex != null) {
942                 if (tokenType == vertex.getType()
943                     && vertex.getLineNo() <= endLineNumber) {
944                     result.add(vertex);
945                 }
946                 if (vertex.getNextSibling() != null) {
947                     stack.push(vertex.getNextSibling());
948                 }
949                 vertex = vertex.getFirstChild();
950             }
951         }
952         return result;
953     }
954 
955     /**
956      * Collects all tokens which are equal to current token starting with the current ast node and
957      * which line number is lower or equal to the end line number.
958      * @param ast ast node.
959      * @param token token.
960      * @param endLineNumber end line number.
961      * @return a set of tokens which are equal to current token starting with the current ast node
962      *         and which line number is lower or equal to the end line number.
963      */
964     private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token,
965                                                                      int endLineNumber) {
966         DetailAST vertex = ast;
967         final Set<DetailAST> result = new HashSet<>();
968         final Deque<DetailAST> stack = new ArrayDeque<>();
969         while (vertex != null || !stack.isEmpty()) {
970             if (!stack.isEmpty()) {
971                 vertex = stack.pop();
972             }
973             while (vertex != null) {
974                 if (token.equals(vertex)
975                         && vertex.getLineNo() <= endLineNumber) {
976                     result.add(vertex);
977                 }
978                 if (vertex.getNextSibling() != null) {
979                     stack.push(vertex.getNextSibling());
980                 }
981                 vertex = vertex.getFirstChild();
982             }
983         }
984         return result;
985     }
986 
987     /**
988      * Returns the frame where the method is declared, if the given method is used without
989      * 'this' and null otherwise.
990      * @param ast the IDENT ast of the name to check.
991      * @return the frame where the method is declared, if the given method is used without
992      *         'this' and null otherwise.
993      */
994     private AbstractFrame getMethodWithoutThis(DetailAST ast) {
995         AbstractFrame result = null;
996         if (!validateOnlyOverlapping) {
997             final AbstractFrame frame = findFrame(ast, true);
998             if (frame != null
999                     && ((ClassFrame) frame).hasInstanceMethod(ast)
1000                     && !((ClassFrame) frame).hasStaticMethod(ast)) {
1001                 result = frame;
1002             }
1003         }
1004         return result;
1005     }
1006 
1007     /**
1008      * Find the class frame containing declaration.
1009      * @param name IDENT ast of the declaration to find.
1010      * @param lookForMethod whether we are looking for a method name.
1011      * @return AbstractFrame containing declaration or null.
1012      */
1013     private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) {
1014         AbstractFrame frame = current.peek();
1015 
1016         while (true) {
1017             frame = findFrame(frame, name, lookForMethod);
1018 
1019             if (frame == null || frame instanceof ClassFrame) {
1020                 break;
1021             }
1022 
1023             frame = frame.getParent();
1024         }
1025 
1026         return frame;
1027     }
1028 
1029     /**
1030      * Find frame containing declaration.
1031      * @param name IDENT ast of the declaration to find.
1032      * @param lookForMethod whether we are looking for a method name.
1033      * @return AbstractFrame containing declaration or null.
1034      */
1035     private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
1036         return findFrame(current.peek(), name, lookForMethod);
1037     }
1038 
1039     /**
1040      * Find frame containing declaration.
1041      * @param frame The parent frame to searching in.
1042      * @param name IDENT ast of the declaration to find.
1043      * @param lookForMethod whether we are looking for a method name.
1044      * @return AbstractFrame containing declaration or null.
1045      */
1046     private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name,
1047             boolean lookForMethod) {
1048         return frame.getIfContains(name, lookForMethod);
1049     }
1050 
1051     /**
1052      * Check that token is related to Definition tokens.
1053      * @param parentType token Type.
1054      * @return true if token is related to Definition Tokens.
1055      */
1056     private static boolean isDeclarationToken(int parentType) {
1057         return DECLARATION_TOKENS.contains(parentType);
1058     }
1059 
1060     /**
1061      * Check that token is related to assign tokens.
1062      * @param tokenType token type.
1063      * @return true if token is related to assign tokens.
1064      */
1065     private static boolean isAssignToken(int tokenType) {
1066         return ASSIGN_TOKENS.contains(tokenType);
1067     }
1068 
1069     /**
1070      * Check that token is related to compound assign tokens.
1071      * @param tokenType token type.
1072      * @return true if token is related to compound assign tokens.
1073      */
1074     private static boolean isCompoundAssignToken(int tokenType) {
1075         return COMPOUND_ASSIGN_TOKENS.contains(tokenType);
1076     }
1077 
1078     /**
1079      * Gets the name of the nearest parent ClassFrame.
1080      * @return the name of the nearest parent ClassFrame.
1081      */
1082     private String getNearestClassFrameName() {
1083         AbstractFrame frame = current.peek();
1084         while (frame.getType() != FrameType.CLASS_FRAME) {
1085             frame = frame.getParent();
1086         }
1087         return frame.getFrameName();
1088     }
1089 
1090     /**
1091      * Checks if the token is a Lambda parameter.
1092      * @param ast the {@code DetailAST} value of the token to be checked
1093      * @return true if the token is a Lambda parameter
1094      */
1095     private static boolean isLambdaParameter(DetailAST ast) {
1096         DetailAST parent;
1097         for (parent = ast.getParent(); parent != null; parent = parent.getParent()) {
1098             if (parent.getType() == TokenTypes.LAMBDA) {
1099                 break;
1100             }
1101         }
1102         final boolean isLambdaParameter;
1103         if (parent == null) {
1104             isLambdaParameter = false;
1105         }
1106         else if (ast.getType() == TokenTypes.PARAMETER_DEF) {
1107             isLambdaParameter = true;
1108         }
1109         else {
1110             final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS);
1111             if (lambdaParameters == null) {
1112                 isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText());
1113             }
1114             else {
1115                 isLambdaParameter = TokenUtil.findFirstTokenByPredicate(lambdaParameters,
1116                     paramDef -> {
1117                         final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT);
1118                         return param != null && param.getText().equals(ast.getText());
1119                     }).isPresent();
1120             }
1121         }
1122         return isLambdaParameter;
1123     }
1124 
1125     /** An AbstractFrame type. */
1126     private enum FrameType {
1127 
1128         /** Class frame type. */
1129         CLASS_FRAME,
1130         /** Constructor frame type. */
1131         CTOR_FRAME,
1132         /** Method frame type. */
1133         METHOD_FRAME,
1134         /** Block frame type. */
1135         BLOCK_FRAME,
1136         /** Catch frame type. */
1137         CATCH_FRAME,
1138         /** Lambda frame type. */
1139         FOR_FRAME,
1140 
1141     }
1142 
1143     /**
1144      * A declaration frame.
1145      */
1146     private abstract static class AbstractFrame {
1147 
1148         /** Set of name of variables declared in this frame. */
1149         private final Set<DetailAST> varIdents;
1150 
1151         /** Parent frame. */
1152         private final AbstractFrame parent;
1153 
1154         /** Name identifier token. */
1155         private final DetailAST frameNameIdent;
1156 
1157         /**
1158          * Constructor -- invocable only via super() from subclasses.
1159          * @param parent parent frame.
1160          * @param ident frame name ident.
1161          */
1162         protected AbstractFrame(AbstractFrame parent, DetailAST ident) {
1163             this.parent = parent;
1164             frameNameIdent = ident;
1165             varIdents = new HashSet<>();
1166         }
1167 
1168         /**
1169          * Get the type of the frame.
1170          * @return a FrameType.
1171          */
1172         protected abstract FrameType getType();
1173 
1174         /**
1175          * Add a name to the frame.
1176          * @param identToAdd the name we're adding.
1177          */
1178         private void addIdent(DetailAST identToAdd) {
1179             varIdents.add(identToAdd);
1180         }
1181 
1182         protected AbstractFrame getParent() {
1183             return parent;
1184         }
1185 
1186         protected String getFrameName() {
1187             return frameNameIdent.getText();
1188         }
1189 
1190         public DetailAST getFrameNameIdent() {
1191             return frameNameIdent;
1192         }
1193 
1194         /**
1195          * Check whether the frame contains a field or a variable with the given name.
1196          * @param nameToFind the IDENT ast of the name we're looking for.
1197          * @return whether it was found.
1198          */
1199         protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1200             return containsFieldOrVariableDef(varIdents, nameToFind);
1201         }
1202 
1203         /**
1204          * Check whether the frame contains a given name.
1205          * @param nameToFind IDENT ast of the name we're looking for.
1206          * @param lookForMethod whether we are looking for a method name.
1207          * @return whether it was found.
1208          */
1209         protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1210             final AbstractFrame frame;
1211 
1212             if (!lookForMethod
1213                 && containsFieldOrVariable(nameToFind)) {
1214                 frame = this;
1215             }
1216             else {
1217                 frame = parent.getIfContains(nameToFind, lookForMethod);
1218             }
1219             return frame;
1220         }
1221 
1222         /**
1223          * Whether the set contains a declaration with the text of the specified
1224          * IDENT ast and it is declared in a proper position.
1225          * @param set the set of declarations.
1226          * @param ident the specified IDENT ast.
1227          * @return true if the set contains a declaration with the text of the specified
1228          *         IDENT ast and it is declared in a proper position.
1229          */
1230         protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
1231             boolean result = false;
1232             for (DetailAST ast: set) {
1233                 if (isProperDefinition(ident, ast)) {
1234                     result = true;
1235                     break;
1236                 }
1237             }
1238             return result;
1239         }
1240 
1241         /**
1242          * Whether the definition is correspondent to the IDENT.
1243          * @param ident the IDENT ast to check.
1244          * @param ast the IDENT ast of the definition to check.
1245          * @return true if ast is correspondent to ident.
1246          */
1247         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1248             final String nameToFind = ident.getText();
1249             return nameToFind.equals(ast.getText())
1250                 && checkPosition(ast, ident);
1251         }
1252 
1253         /**
1254          * Whether the declaration is located before the checked ast.
1255          * @param ast1 the IDENT ast of the declaration.
1256          * @param ast2 the IDENT ast to check.
1257          * @return true, if the declaration is located before the checked ast.
1258          */
1259         private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
1260             boolean result = false;
1261             if (ast1.getLineNo() < ast2.getLineNo()
1262                     || ast1.getLineNo() == ast2.getLineNo()
1263                     && ast1.getColumnNo() < ast2.getColumnNo()) {
1264                 result = true;
1265             }
1266             return result;
1267         }
1268 
1269     }
1270 
1271     /**
1272      * A frame initiated at method definition; holds a method definition token.
1273      */
1274     private static class MethodFrame extends AbstractFrame {
1275 
1276         /**
1277          * Creates method frame.
1278          * @param parent parent frame.
1279          * @param ident method name identifier token.
1280          */
1281         protected MethodFrame(AbstractFrame parent, DetailAST ident) {
1282             super(parent, ident);
1283         }
1284 
1285         @Override
1286         protected FrameType getType() {
1287             return FrameType.METHOD_FRAME;
1288         }
1289 
1290     }
1291 
1292     /**
1293      * A frame initiated at constructor definition.
1294      */
1295     private static class ConstructorFrame extends AbstractFrame {
1296 
1297         /**
1298          * Creates a constructor frame.
1299          * @param parent parent frame.
1300          * @param ident frame name ident.
1301          */
1302         protected ConstructorFrame(AbstractFrame parent, DetailAST ident) {
1303             super(parent, ident);
1304         }
1305 
1306         @Override
1307         protected FrameType getType() {
1308             return FrameType.CTOR_FRAME;
1309         }
1310 
1311     }
1312 
1313     /**
1314      * A frame initiated at class, enum or interface definition; holds instance variable names.
1315      */
1316     private static class ClassFrame extends AbstractFrame {
1317 
1318         /** Set of idents of instance members declared in this frame. */
1319         private final Set<DetailAST> instanceMembers;
1320         /** Set of idents of instance methods declared in this frame. */
1321         private final Set<DetailAST> instanceMethods;
1322         /** Set of idents of variables declared in this frame. */
1323         private final Set<DetailAST> staticMembers;
1324         /** Set of idents of static methods declared in this frame. */
1325         private final Set<DetailAST> staticMethods;
1326 
1327         /**
1328          * Creates new instance of ClassFrame.
1329          * @param parent parent frame.
1330          * @param ident frame name ident.
1331          */
1332         /* package */ ClassFrame(AbstractFrame parent, DetailAST ident) {
1333             super(parent, ident);
1334             instanceMembers = new HashSet<>();
1335             instanceMethods = new HashSet<>();
1336             staticMembers = new HashSet<>();
1337             staticMethods = new HashSet<>();
1338         }
1339 
1340         @Override
1341         protected FrameType getType() {
1342             return FrameType.CLASS_FRAME;
1343         }
1344 
1345         /**
1346          * Adds static member's ident.
1347          * @param ident an ident of static member of the class.
1348          */
1349         public void addStaticMember(final DetailAST ident) {
1350             staticMembers.add(ident);
1351         }
1352 
1353         /**
1354          * Adds static method's name.
1355          * @param ident an ident of static method of the class.
1356          */
1357         public void addStaticMethod(final DetailAST ident) {
1358             staticMethods.add(ident);
1359         }
1360 
1361         /**
1362          * Adds instance member's ident.
1363          * @param ident an ident of instance member of the class.
1364          */
1365         public void addInstanceMember(final DetailAST ident) {
1366             instanceMembers.add(ident);
1367         }
1368 
1369         /**
1370          * Adds instance method's name.
1371          * @param ident an ident of instance method of the class.
1372          */
1373         public void addInstanceMethod(final DetailAST ident) {
1374             instanceMethods.add(ident);
1375         }
1376 
1377         /**
1378          * Checks if a given name is a known instance member of the class.
1379          * @param ident the IDENT ast of the name to check.
1380          * @return true is the given name is a name of a known
1381          *         instance member of the class.
1382          */
1383         public boolean hasInstanceMember(final DetailAST ident) {
1384             return containsFieldOrVariableDef(instanceMembers, ident);
1385         }
1386 
1387         /**
1388          * Checks if a given name is a known instance method of the class.
1389          * @param ident the IDENT ast of the method call to check.
1390          * @return true if the given ast is correspondent to a known
1391          *         instance method of the class.
1392          */
1393         public boolean hasInstanceMethod(final DetailAST ident) {
1394             return containsMethodDef(instanceMethods, ident);
1395         }
1396 
1397         /**
1398          * Checks if a given name is a known static method of the class.
1399          * @param ident the IDENT ast of the method call to check.
1400          * @return true is the given ast is correspondent to a known
1401          *         instance method of the class.
1402          */
1403         public boolean hasStaticMethod(final DetailAST ident) {
1404             return containsMethodDef(staticMethods, ident);
1405         }
1406 
1407         /**
1408          * Checks whether given instance member has final modifier.
1409          * @param instanceMember an instance member of a class.
1410          * @return true if given instance member has final modifier.
1411          */
1412         public boolean hasFinalField(final DetailAST instanceMember) {
1413             boolean result = false;
1414             for (DetailAST member : instanceMembers) {
1415                 final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS);
1416                 final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null;
1417                 if (finalMod && member.equals(instanceMember)) {
1418                     result = true;
1419                     break;
1420                 }
1421             }
1422             return result;
1423         }
1424 
1425         @Override
1426         protected boolean containsFieldOrVariable(DetailAST nameToFind) {
1427             return containsFieldOrVariableDef(instanceMembers, nameToFind)
1428                     || containsFieldOrVariableDef(staticMembers, nameToFind);
1429         }
1430 
1431         @Override
1432         protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
1433             final String nameToFind = ident.getText();
1434             return nameToFind.equals(ast.getText());
1435         }
1436 
1437         @Override
1438         protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
1439             AbstractFrame frame = null;
1440 
1441             if (lookForMethod && containsMethod(nameToFind)
1442                 || containsFieldOrVariable(nameToFind)) {
1443                 frame = this;
1444             }
1445             else if (getParent() != null) {
1446                 frame = getParent().getIfContains(nameToFind, lookForMethod);
1447             }
1448             return frame;
1449         }
1450 
1451         /**
1452          * Check whether the frame contains a given method.
1453          * @param methodToFind the AST of the method to find.
1454          * @return true, if a method with the same name and number of parameters is found.
1455          */
1456         private boolean containsMethod(DetailAST methodToFind) {
1457             return containsMethodDef(instanceMethods, methodToFind)
1458                 || containsMethodDef(staticMethods, methodToFind);
1459         }
1460 
1461         /**
1462          * Whether the set contains a method definition with the
1463          *     same name and number of parameters.
1464          * @param set the set of definitions.
1465          * @param ident the specified method call IDENT ast.
1466          * @return true if the set contains a definition with the
1467          *     same name and number of parameters.
1468          */
1469         private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
1470             boolean result = false;
1471             for (DetailAST ast: set) {
1472                 if (isSimilarSignature(ident, ast)) {
1473                     result = true;
1474                     break;
1475                 }
1476             }
1477             return result;
1478         }
1479 
1480         /**
1481          * Whether the method definition has the same name and number of parameters.
1482          * @param ident the specified method call IDENT ast.
1483          * @param ast the ast of a method definition to compare with.
1484          * @return true if a method definition has the same name and number of parameters
1485          *     as the method call.
1486          */
1487         private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
1488             boolean result = false;
1489             final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST);
1490             if (elistToken != null && ident.getText().equals(ast.getText())) {
1491                 final int paramsNumber =
1492                     ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount();
1493                 final int argsNumber = elistToken.getChildCount();
1494                 result = paramsNumber == argsNumber;
1495             }
1496             return result;
1497         }
1498 
1499     }
1500 
1501     /**
1502      * An anonymous class frame; holds instance variable names.
1503      */
1504     private static class AnonymousClassFrame extends ClassFrame {
1505 
1506         /** The name of the frame. */
1507         private final String frameName;
1508 
1509         /**
1510          * Creates anonymous class frame.
1511          * @param parent parent frame.
1512          * @param frameName name of the frame.
1513          */
1514         protected AnonymousClassFrame(AbstractFrame parent, String frameName) {
1515             super(parent, null);
1516             this.frameName = frameName;
1517         }
1518 
1519         @Override
1520         protected String getFrameName() {
1521             return frameName;
1522         }
1523 
1524     }
1525 
1526     /**
1527      * A frame initiated on entering a statement list; holds local variable names.
1528      */
1529     private static class BlockFrame extends AbstractFrame {
1530 
1531         /**
1532          * Creates block frame.
1533          * @param parent parent frame.
1534          * @param ident ident frame name ident.
1535          */
1536         protected BlockFrame(AbstractFrame parent, DetailAST ident) {
1537             super(parent, ident);
1538         }
1539 
1540         @Override
1541         protected FrameType getType() {
1542             return FrameType.BLOCK_FRAME;
1543         }
1544 
1545     }
1546 
1547     /**
1548      * A frame initiated on entering a catch block; holds local catch variable names.
1549      */
1550     private static class CatchFrame extends AbstractFrame {
1551 
1552         /**
1553          * Creates catch frame.
1554          * @param parent parent frame.
1555          * @param ident ident frame name ident.
1556          */
1557         protected CatchFrame(AbstractFrame parent, DetailAST ident) {
1558             super(parent, ident);
1559         }
1560 
1561         @Override
1562         public FrameType getType() {
1563             return FrameType.CATCH_FRAME;
1564         }
1565 
1566     }
1567 
1568     /**
1569      * A frame initiated on entering a for block; holds local for variable names.
1570      */
1571     private static class ForFrame extends AbstractFrame {
1572 
1573         /**
1574          * Creates for frame.
1575          * @param parent parent frame.
1576          * @param ident ident frame name ident.
1577          */
1578         protected ForFrame(AbstractFrame parent, DetailAST ident) {
1579             super(parent, ident);
1580         }
1581 
1582         @Override
1583         public FrameType getType() {
1584             return FrameType.FOR_FRAME;
1585         }
1586 
1587     }
1588 
1589 }