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