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