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