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