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