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