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