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