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