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