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