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