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