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