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