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