001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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.Deque; 023import java.util.Map; 024import java.util.Queue; 025import java.util.Set; 026 027import com.google.common.collect.ImmutableSet; 028import com.google.common.collect.Lists; 029import com.google.common.collect.Maps; 030import com.google.common.collect.Queues; 031import com.google.common.collect.Sets; 032import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 033import com.puppycrawl.tools.checkstyle.api.DetailAST; 034import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035import com.puppycrawl.tools.checkstyle.utils.CheckUtils; 036import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 037 038/** 039 * <p>Checks that code doesn't rely on the "this" default. 040 * That is references to instance variables and methods of the present 041 * object are explicitly of the form "this.varName" or 042 * "this.methodName(args)". 043 * </p> 044 * Check has the following options: 045 * <p><b>checkFields</b> - whether to check references to fields. Default value is <b>true</b>.</p> 046 * <p><b>checkMethods</b> - whether to check references to methods. 047 * Default value is <b>true</b>.</p> 048 * <p><b>validateOnlyOverlapping</b> - whether to check only overlapping by variables or 049 * arguments. Default value is <b>true</b>.</p> 050 * 051 * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false' 052 * and not that actual nowadays.</p> 053 * 054 * <p>Examples of use: 055 * <pre> 056 * <module name="RequireThis"/> 057 * </pre> 058 * An example of how to configure to check {@code this} qualifier for 059 * methods only: 060 * <pre> 061 * <module name="RequireThis"> 062 * <property name="checkFields" value="false"/> 063 * <property name="checkMethods" value="true"/> 064 * </module> 065 * </pre> 066 * 067 * <p>Rationale:</p> 068 * <ol> 069 * <li> 070 * The same notation/habit for C++ and Java (C++ have global methods, so having 071 * "this." do make sense in it to distinguish call of method of class 072 * instead of global). 073 * </li> 074 * <li> 075 * Non-IDE development (ease of refactoring, some clearness to distinguish 076 * static and non-static methods). 077 * </li> 078 * </ol> 079 * 080 * <p>Limitations: Nothing is currently done about static variables 081 * or catch-blocks. Static methods invoked on a class name seem to be OK; 082 * both the class name and the method name have a DOT parent. 083 * Non-static methods invoked on either this or a variable name seem to be 084 * OK, likewise.</p> 085 * 086 * @author Stephen Bloch 087 * @author o_sukhodolsky 088 * @author Andrei Selkin 089 */ 090public class RequireThisCheck extends AbstractCheck { 091 092 /** 093 * A key is pointing to the warning message text in "messages.properties" 094 * file. 095 */ 096 public static final String MSG_METHOD = "require.this.method"; 097 /** 098 * A key is pointing to the warning message text in "messages.properties" 099 * file. 100 */ 101 public static final String MSG_VARIABLE = "require.this.variable"; 102 103 /** Set of all declaration tokens. */ 104 private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of( 105 TokenTypes.VARIABLE_DEF, 106 TokenTypes.CTOR_DEF, 107 TokenTypes.METHOD_DEF, 108 TokenTypes.CLASS_DEF, 109 TokenTypes.ENUM_DEF, 110 TokenTypes.INTERFACE_DEF, 111 TokenTypes.PARAMETER_DEF, 112 TokenTypes.TYPE_ARGUMENT 113 ); 114 /** Set of all assign tokens. */ 115 private static final ImmutableSet<Integer> ASSIGN_TOKENS = ImmutableSet.of( 116 TokenTypes.ASSIGN, 117 TokenTypes.PLUS_ASSIGN, 118 TokenTypes.STAR_ASSIGN, 119 TokenTypes.DIV_ASSIGN, 120 TokenTypes.MOD_ASSIGN, 121 TokenTypes.SR_ASSIGN, 122 TokenTypes.BSR_ASSIGN, 123 TokenTypes.SL_ASSIGN, 124 TokenTypes.BAND_ASSIGN, 125 TokenTypes.BXOR_ASSIGN 126 ); 127 /** Set of all compound assign tokens. */ 128 private static final ImmutableSet<Integer> COMPOUND_ASSIGN_TOKENS = ImmutableSet.of( 129 TokenTypes.PLUS_ASSIGN, 130 TokenTypes.STAR_ASSIGN, 131 TokenTypes.DIV_ASSIGN, 132 TokenTypes.MOD_ASSIGN, 133 TokenTypes.SR_ASSIGN, 134 TokenTypes.BSR_ASSIGN, 135 TokenTypes.SL_ASSIGN, 136 TokenTypes.BAND_ASSIGN, 137 TokenTypes.BXOR_ASSIGN 138 ); 139 140 /** Tree of all the parsed frames. */ 141 private Map<DetailAST, AbstractFrame> frames; 142 143 /** Frame for the currently processed AST. */ 144 private AbstractFrame current; 145 146 /** Whether we should check fields usage. */ 147 private boolean checkFields = true; 148 /** Whether we should check methods usage. */ 149 private boolean checkMethods = true; 150 /** Whether we should check only overlapping by variables or arguments. */ 151 private boolean validateOnlyOverlapping = true; 152 153 /** 154 * Setter for checkFields property. 155 * @param checkFields should we check fields usage or not. 156 */ 157 public void setCheckFields(boolean checkFields) { 158 this.checkFields = checkFields; 159 } 160 161 /** 162 * Setter for checkMethods property. 163 * @param checkMethods should we check methods usage or not. 164 */ 165 public void setCheckMethods(boolean checkMethods) { 166 this.checkMethods = checkMethods; 167 } 168 169 /** 170 * Setter for validateOnlyOverlapping property. 171 * @param validateOnlyOverlapping should we check only overlapping by variables or arguments. 172 */ 173 public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) { 174 this.validateOnlyOverlapping = validateOnlyOverlapping; 175 } 176 177 @Override 178 public int[] getDefaultTokens() { 179 return getAcceptableTokens(); 180 } 181 182 @Override 183 public int[] getRequiredTokens() { 184 return getAcceptableTokens(); 185 } 186 187 @Override 188 public int[] getAcceptableTokens() { 189 return new int[] { 190 TokenTypes.CLASS_DEF, 191 TokenTypes.INTERFACE_DEF, 192 TokenTypes.ENUM_DEF, 193 TokenTypes.CTOR_DEF, 194 TokenTypes.METHOD_DEF, 195 TokenTypes.SLIST, 196 TokenTypes.IDENT, 197 }; 198 } 199 200 @Override 201 public void beginTree(DetailAST rootAST) { 202 frames = Maps.newHashMap(); 203 current = null; 204 205 final Deque<AbstractFrame> frameStack = Lists.newLinkedList(); 206 DetailAST curNode = rootAST; 207 while (curNode != null) { 208 collectDeclarations(frameStack, curNode); 209 DetailAST toVisit = curNode.getFirstChild(); 210 while (curNode != null && toVisit == null) { 211 endCollectingDeclarations(frameStack, curNode); 212 toVisit = curNode.getNextSibling(); 213 if (toVisit == null) { 214 curNode = curNode.getParent(); 215 } 216 } 217 curNode = toVisit; 218 } 219 } 220 221 @Override 222 public void visitToken(DetailAST ast) { 223 switch (ast.getType()) { 224 case TokenTypes.IDENT : 225 processIdent(ast); 226 break; 227 case TokenTypes.CLASS_DEF : 228 case TokenTypes.INTERFACE_DEF : 229 case TokenTypes.ENUM_DEF : 230 case TokenTypes.ANNOTATION_DEF : 231 case TokenTypes.SLIST : 232 case TokenTypes.METHOD_DEF : 233 case TokenTypes.CTOR_DEF : 234 current = frames.get(ast); 235 break; 236 default : 237 // do nothing 238 } 239 } 240 241 /** 242 * Checks if a given IDENT is method call or field name which 243 * requires explicit {@code this} qualifier. 244 * @param ast IDENT to check. 245 */ 246 private void processIdent(DetailAST ast) { 247 final int parentType = ast.getParent().getType(); 248 switch (parentType) { 249 case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR: 250 case TokenTypes.ANNOTATION: 251 case TokenTypes.ANNOTATION_FIELD_DEF: 252 // no need to check annotations content 253 break; 254 case TokenTypes.METHOD_CALL: 255 if (checkMethods) { 256 final AbstractFrame frame = getMethodWithoutThis(ast); 257 if (frame != null) { 258 logViolation(MSG_METHOD, ast, frame); 259 } 260 } 261 break; 262 default: 263 if (checkFields) { 264 final AbstractFrame frame = getFieldWithoutThis(ast, parentType); 265 if (frame != null) { 266 logViolation(MSG_VARIABLE, ast, frame); 267 } 268 } 269 break; 270 } 271 } 272 273 /** 274 * Helper method to log a LocalizedMessage. 275 * @param ast a node to get line id column numbers associated with the message. 276 * @param msgKey key to locale message format. 277 * @param frame the class frame where the violation is found. 278 */ 279 private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) { 280 if (frame.getFrameName().equals(getNearestClassFrameName())) { 281 log(ast, msgKey, ast.getText(), ""); 282 } 283 else { 284 log(ast, msgKey, ast.getText(), frame.getFrameName() + '.'); 285 } 286 } 287 288 /** 289 * Returns the frame where the field is declared, if the given field is used without 290 * 'this', and null otherwise. 291 * @param ast field definition ast token. 292 * @param parentType type of the parent. 293 * @return the frame where the field is declared, if the given field is used without 294 * 'this' and null otherwise. 295 */ 296 private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) { 297 final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null; 298 final boolean methodNameInMethodCall = parentType == TokenTypes.DOT 299 && ast.getPreviousSibling() != null; 300 final boolean typeName = parentType == TokenTypes.TYPE 301 || parentType == TokenTypes.LITERAL_NEW; 302 AbstractFrame frame = null; 303 304 if (!importOrPackage 305 && !methodNameInMethodCall 306 && !typeName 307 && !isDeclarationToken(parentType)) { 308 frame = getClassFrameWhereViolationIsFound(ast); 309 } 310 return frame; 311 } 312 313 /** 314 * Parses the next AST for declarations. 315 * @param frameStack stack containing the FrameTree being built. 316 * @param ast AST to parse. 317 */ 318 private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) { 319 final AbstractFrame frame = frameStack.peek(); 320 switch (ast.getType()) { 321 case TokenTypes.VARIABLE_DEF : 322 collectVariableDeclarations(ast, frame); 323 break; 324 case TokenTypes.PARAMETER_DEF : 325 if (!CheckUtils.isReceiverParameter(ast)) { 326 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT); 327 frame.addIdent(parameterIdent); 328 } 329 break; 330 case TokenTypes.CLASS_DEF : 331 case TokenTypes.INTERFACE_DEF : 332 case TokenTypes.ENUM_DEF : 333 case TokenTypes.ANNOTATION_DEF : 334 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 335 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent)); 336 break; 337 case TokenTypes.SLIST : 338 frameStack.addFirst(new BlockFrame(frame, ast)); 339 break; 340 case TokenTypes.METHOD_DEF : 341 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 342 if (frame.getType() == FrameType.CLASS_FRAME) { 343 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS); 344 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 345 ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent); 346 } 347 else { 348 ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent); 349 } 350 } 351 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent)); 352 break; 353 case TokenTypes.CTOR_DEF : 354 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 355 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent)); 356 break; 357 default: 358 // do nothing 359 } 360 } 361 362 /** 363 * Collects variable declarations. 364 * @param ast variable token. 365 * @param frame current frame. 366 */ 367 private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) { 368 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 369 if (frame.getType() == FrameType.CLASS_FRAME) { 370 final DetailAST mods = 371 ast.findFirstToken(TokenTypes.MODIFIERS); 372 if (ScopeUtils.isInInterfaceBlock(ast) 373 || mods.branchContains(TokenTypes.LITERAL_STATIC)) { 374 ((ClassFrame) frame).addStaticMember(ident); 375 } 376 else { 377 ((ClassFrame) frame).addInstanceMember(ident); 378 } 379 } 380 else { 381 frame.addIdent(ident); 382 } 383 } 384 385 /** 386 * Ends parsing of the AST for declarations. 387 * @param frameStack Stack containing the FrameTree being built. 388 * @param ast AST that was parsed. 389 */ 390 private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) { 391 switch (ast.getType()) { 392 case TokenTypes.CLASS_DEF : 393 case TokenTypes.INTERFACE_DEF : 394 case TokenTypes.ENUM_DEF : 395 case TokenTypes.ANNOTATION_DEF : 396 case TokenTypes.SLIST : 397 case TokenTypes.METHOD_DEF : 398 case TokenTypes.CTOR_DEF : 399 frames.put(ast, frameStack.poll()); 400 break; 401 default : 402 // do nothing 403 } 404 } 405 406 /** 407 * Returns the class frame where violation is found (where the field is used without 'this') 408 * or null otherwise. 409 * @param ast IDENT ast to check. 410 * @return the class frame where violation is found or null otherwise. 411 */ 412 private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) { 413 AbstractFrame frameWhereViolationIsFound = null; 414 final AbstractFrame variableDeclarationFrame = findFrame(ast, false); 415 if (variableDeclarationFrame != null) { 416 final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType(); 417 final DetailAST prevSibling = ast.getPreviousSibling(); 418 if (variableDeclarationFrameType == FrameType.CLASS_FRAME 419 && !validateOnlyOverlapping 420 && prevSibling == null 421 && !ScopeUtils.isInInterfaceBlock(ast) 422 && canBeReferencedFromStaticContext(ast)) { 423 frameWhereViolationIsFound = variableDeclarationFrame; 424 } 425 else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) { 426 if (isOverlappingByArgument(ast)) { 427 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 428 && !isReturnedVariable(variableDeclarationFrame, ast) 429 && canBeReferencedFromStaticContext(ast) 430 && canAssignValueToClassField(ast)) { 431 frameWhereViolationIsFound = findFrame(ast, true); 432 } 433 } 434 else if (!validateOnlyOverlapping 435 && prevSibling == null 436 && isAssignToken(ast.getParent().getType()) 437 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 438 && canBeReferencedFromStaticContext(ast) 439 && canAssignValueToClassField(ast)) { 440 frameWhereViolationIsFound = findFrame(ast, true); 441 442 } 443 } 444 else if (variableDeclarationFrameType == FrameType.CTOR_FRAME 445 && isOverlappingByArgument(ast) 446 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) { 447 frameWhereViolationIsFound = findFrame(ast, true); 448 } 449 else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME) { 450 if (isOverlappingByLocalVariable(ast)) { 451 if (canAssignValueToClassField(ast) 452 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 453 && !isReturnedVariable(variableDeclarationFrame, ast) 454 && canBeReferencedFromStaticContext(ast)) { 455 frameWhereViolationIsFound = findFrame(ast, true); 456 } 457 } 458 else if (!validateOnlyOverlapping 459 && prevSibling == null 460 && isAssignToken(ast.getParent().getType()) 461 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 462 && canBeReferencedFromStaticContext(ast)) { 463 frameWhereViolationIsFound = findFrame(ast, true); 464 } 465 } 466 } 467 return frameWhereViolationIsFound; 468 } 469 470 /** 471 * Checks whether user arranges 'this' for variable in method, constructor, or block on his own. 472 * @param currentFrame current frame. 473 * @param ident ident token. 474 * @return true if user arranges 'this' for variable in method, constructor, 475 * or block on his own. 476 */ 477 private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame, 478 DetailAST ident) { 479 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 480 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 481 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 482 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 483 484 final Set<DetailAST> variableUsagesInsideBlock = 485 getAllTokensWhichAreEqualToCurrent(definitionToken, ident, blockEndToken.getLineNo()); 486 487 boolean userDefinedArrangementOfThis = false; 488 for (DetailAST variableUsage : variableUsagesInsideBlock) { 489 final DetailAST prevSibling = variableUsage.getPreviousSibling(); 490 if (prevSibling != null 491 && prevSibling.getType() == TokenTypes.LITERAL_THIS) { 492 userDefinedArrangementOfThis = true; 493 } 494 } 495 return userDefinedArrangementOfThis; 496 } 497 498 /** 499 * Returns the token which ends the code block. 500 * @param blockNameIdent block name identifier. 501 * @param blockStartToken token which starts the block. 502 * @return the token which ends the code block. 503 */ 504 private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) { 505 final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent, TokenTypes.RCURLY); 506 DetailAST blockEndToken = null; 507 for (DetailAST currentRcurly : rcurlyTokens) { 508 final DetailAST parent = currentRcurly.getParent(); 509 if (blockStartToken.getLineNo() == parent.getLineNo()) { 510 blockEndToken = currentRcurly; 511 } 512 } 513 return blockEndToken; 514 } 515 516 /** 517 * Checks whether the current variable is returned from the method. 518 * @param currentFrame current frame. 519 * @param ident variable ident token. 520 * @return true if the current variable is returned from the method. 521 */ 522 private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) { 523 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 524 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 525 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 526 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 527 528 final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken, 529 TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo()); 530 531 boolean returnedVariable = false; 532 for (DetailAST returnToken : returnsInsideBlock) { 533 returnedVariable = returnToken.findAll(ident).hasMoreNodes(); 534 if (returnedVariable) { 535 break; 536 } 537 } 538 return returnedVariable; 539 } 540 541 /** 542 * Checks whether a field can be referenced from a static context. 543 * @param ident ident token. 544 * @return true if field can be referenced from a static context. 545 */ 546 private boolean canBeReferencedFromStaticContext(DetailAST ident) { 547 AbstractFrame variableDeclarationFrame = findFrame(ident, false); 548 boolean staticInitializationBlock = false; 549 while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME) { 550 final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 551 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 552 if (definitionToken.getType() == TokenTypes.STATIC_INIT) { 553 staticInitializationBlock = true; 554 break; 555 } 556 variableDeclarationFrame = variableDeclarationFrame.getParent(); 557 } 558 559 boolean staticContext = false; 560 if (staticInitializationBlock) { 561 staticContext = true; 562 } 563 else { 564 if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) { 565 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident); 566 if (codeBlockDefinition != null) { 567 final DetailAST modifiers = codeBlockDefinition.getFirstChild(); 568 staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT 569 || modifiers.branchContains(TokenTypes.LITERAL_STATIC); 570 } 571 } 572 else { 573 final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 574 final DetailAST definitionToken = frameNameIdent.getParent(); 575 staticContext = definitionToken.branchContains(TokenTypes.LITERAL_STATIC); 576 } 577 } 578 return !staticContext; 579 } 580 581 /** 582 * Returns code block definition token for current identifier. 583 * @param ident ident token. 584 * @return code block definition token for current identifier or null if code block 585 * definition was not found. 586 */ 587 private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) { 588 DetailAST parent = ident.getParent(); 589 while (parent != null 590 && parent.getType() != TokenTypes.METHOD_DEF 591 && parent.getType() != TokenTypes.CTOR_DEF 592 && parent.getType() != TokenTypes.STATIC_INIT) { 593 parent = parent.getParent(); 594 } 595 return parent; 596 } 597 598 /** 599 * Checks whether a value can be assigned to a field. 600 * A value can be assigned to a final field only in constructor block. If there is a method 601 * block, value assignment can be performed only to non final field. 602 * @param ast an identifier token. 603 * @return true if a value can be assigned to a field. 604 */ 605 private boolean canAssignValueToClassField(DetailAST ast) { 606 final AbstractFrame fieldUsageFrame = findFrame(ast, false); 607 final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame); 608 609 final AbstractFrame declarationFrame = findFrame(ast, true); 610 boolean finalField = false; 611 if (declarationFrame != null) { 612 finalField = ((ClassFrame) declarationFrame).hasFinalField(ast); 613 } 614 615 return fieldUsageInConstructor || !finalField; 616 } 617 618 /** 619 * Checks whether a field usage frame is inside constructor frame. 620 * @param frame frame, where field is used. 621 * @return true if the field usage frame is inside constructor frame. 622 */ 623 private static boolean isInsideConstructorFrame(AbstractFrame frame) { 624 boolean assignmentInConstructor = false; 625 AbstractFrame fieldUsageFrame = frame; 626 if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) { 627 while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) { 628 fieldUsageFrame = fieldUsageFrame.getParent(); 629 } 630 if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) { 631 assignmentInConstructor = true; 632 } 633 } 634 return assignmentInConstructor; 635 } 636 637 /** 638 * Checks whether an overlapping by method or constructor argument takes place. 639 * @param ast an identifier. 640 * @return true if an overlapping by method or constructor argument takes place. 641 */ 642 private boolean isOverlappingByArgument(DetailAST ast) { 643 boolean overlapping = false; 644 final DetailAST parent = ast.getParent(); 645 final DetailAST sibling = ast.getNextSibling(); 646 if (sibling != null && isAssignToken(parent.getType())) { 647 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true); 648 if (classFrame != null) { 649 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 650 if (isCompoundAssignToken(parent.getType())) { 651 overlapping = true; 652 } 653 else { 654 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 655 } 656 } 657 } 658 return overlapping; 659 } 660 661 /** 662 * Checks whether an overlapping by local variable takes place. 663 * @param ast an identifier. 664 * @return true if an overlapping by local variable takes place. 665 */ 666 private boolean isOverlappingByLocalVariable(DetailAST ast) { 667 boolean overlapping = false; 668 final DetailAST parent = ast.getParent(); 669 final DetailAST sibling = ast.getNextSibling(); 670 if (sibling != null && isAssignToken(parent.getType())) { 671 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true); 672 if (classFrame != null) { 673 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 674 if (classFrame.hasInstanceMember(ast)) { 675 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 676 } 677 } 678 } 679 return overlapping; 680 } 681 682 /** 683 * Collects all tokens of specific type starting with the current ast node. 684 * @param ast ast node. 685 * @param tokenType token type. 686 * @return a set of all tokens of specific type starting with the current ast node. 687 */ 688 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 689 DetailAST vertex = ast; 690 final Set<DetailAST> result = Sets.newHashSet(); 691 final Deque<DetailAST> stack = Queues.newArrayDeque(); 692 while (vertex != null || !stack.isEmpty()) { 693 if (!stack.isEmpty()) { 694 vertex = stack.pop(); 695 } 696 while (vertex != null) { 697 if (vertex.getType() == tokenType) { 698 result.add(vertex); 699 } 700 if (vertex.getNextSibling() != null) { 701 stack.push(vertex.getNextSibling()); 702 } 703 vertex = vertex.getFirstChild(); 704 } 705 } 706 return result; 707 } 708 709 /** 710 * Collects all tokens of specific type starting with the current ast node and which line 711 * number is lower or equal to the end line number. 712 * @param ast ast node. 713 * @param tokenType token type. 714 * @param endLineNumber end line number. 715 * @return a set of all tokens of specific type starting with the current ast node and which 716 * line number is lower or equal to the end line number. 717 */ 718 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType, 719 int endLineNumber) { 720 DetailAST vertex = ast; 721 final Set<DetailAST> result = Sets.newHashSet(); 722 final Deque<DetailAST> stack = Queues.newArrayDeque(); 723 while (vertex != null || !stack.isEmpty()) { 724 if (!stack.isEmpty()) { 725 vertex = stack.pop(); 726 } 727 while (vertex != null) { 728 if (tokenType == vertex.getType() 729 && vertex.getLineNo() <= endLineNumber) { 730 result.add(vertex); 731 } 732 if (vertex.getNextSibling() != null) { 733 stack.push(vertex.getNextSibling()); 734 } 735 vertex = vertex.getFirstChild(); 736 } 737 } 738 return result; 739 } 740 741 /** 742 * Collects all tokens which are equal to current token starting with the current ast node and 743 * which line number is lower or equal to the end line number. 744 * @param ast ast node. 745 * @param token token. 746 * @param endLineNumber end line number. 747 * @return a set of tokens which are equal to current token starting with the current ast node 748 * and which line number is lower or equal to the end line number. 749 */ 750 private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token, 751 int endLineNumber) { 752 DetailAST vertex = ast; 753 final Set<DetailAST> result = Sets.newHashSet(); 754 final Deque<DetailAST> stack = Queues.newArrayDeque(); 755 while (vertex != null || !stack.isEmpty()) { 756 if (!stack.isEmpty()) { 757 vertex = stack.pop(); 758 } 759 while (vertex != null) { 760 if (token.equals(vertex) 761 && vertex.getLineNo() <= endLineNumber) { 762 result.add(vertex); 763 } 764 if (vertex.getNextSibling() != null) { 765 stack.push(vertex.getNextSibling()); 766 } 767 vertex = vertex.getFirstChild(); 768 } 769 } 770 return result; 771 } 772 773 /** 774 * Returns the frame where the method is declared, if the given method is used without 775 * 'this' and null otherwise. 776 * @param ast the IDENT ast of the name to check. 777 * @return the frame where the method is declared, if the given method is used without 778 * 'this' and null otherwise. 779 */ 780 private AbstractFrame getMethodWithoutThis(DetailAST ast) { 781 AbstractFrame result = null; 782 final AbstractFrame frame = findFrame(ast, true); 783 if (frame != null 784 && !validateOnlyOverlapping 785 && ((ClassFrame) frame).hasInstanceMethod(ast) 786 && !((ClassFrame) frame).hasStaticMethod(ast)) { 787 result = frame; 788 } 789 return result; 790 } 791 792 /** 793 * Find frame containing declaration. 794 * @param name IDENT ast of the declaration to find. 795 * @param lookForMethod whether we are looking for a method name. 796 * @return AbstractFrame containing declaration or null. 797 */ 798 private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) { 799 final AbstractFrame result; 800 if (current == null) { 801 result = null; 802 } 803 else { 804 result = current.getIfContains(name, lookForMethod); 805 } 806 return result; 807 } 808 809 /** 810 * Check that token is related to Definition tokens. 811 * @param parentType token Type. 812 * @return true if token is related to Definition Tokens. 813 */ 814 private static boolean isDeclarationToken(int parentType) { 815 return DECLARATION_TOKENS.contains(parentType); 816 } 817 818 /** 819 * Check that token is related to assign tokens. 820 * @param tokenType token type. 821 * @return true if token is related to assign tokens. 822 */ 823 private static boolean isAssignToken(int tokenType) { 824 return ASSIGN_TOKENS.contains(tokenType); 825 } 826 827 /** 828 * Check that token is related to compound assign tokens. 829 * @param tokenType token type. 830 * @return true if token is related to compound assign tokens. 831 */ 832 private static boolean isCompoundAssignToken(int tokenType) { 833 return COMPOUND_ASSIGN_TOKENS.contains(tokenType); 834 } 835 836 /** 837 * Gets the name of the nearest parent ClassFrame. 838 * @return the name of the nearest parent ClassFrame. 839 */ 840 private String getNearestClassFrameName() { 841 AbstractFrame frame = current; 842 while (frame.getType() != FrameType.CLASS_FRAME) { 843 frame = frame.getParent(); 844 } 845 return frame.getFrameName(); 846 } 847 848 /** An AbstractFrame type. */ 849 private enum FrameType { 850 /** Class frame type. */ 851 CLASS_FRAME, 852 /** Constructor frame type. */ 853 CTOR_FRAME, 854 /** Method frame type. */ 855 METHOD_FRAME, 856 /** Block frame type. */ 857 BLOCK_FRAME, 858 } 859 860 /** 861 * A declaration frame. 862 * @author Stephen Bloch 863 * @author Andrei Selkin 864 */ 865 private abstract static class AbstractFrame { 866 /** Set of name of variables declared in this frame. */ 867 private final Set<DetailAST> varIdents; 868 869 /** Parent frame. */ 870 private final AbstractFrame parent; 871 872 /** Name identifier token. */ 873 private final DetailAST frameNameIdent; 874 875 /** 876 * Constructor -- invokable only via super() from subclasses. 877 * @param parent parent frame. 878 * @param ident frame name ident. 879 */ 880 protected AbstractFrame(AbstractFrame parent, DetailAST ident) { 881 this.parent = parent; 882 frameNameIdent = ident; 883 varIdents = Sets.newHashSet(); 884 } 885 886 /** 887 * Get the type of the frame. 888 * @return a FrameType. 889 */ 890 protected abstract FrameType getType(); 891 892 /** 893 * Add a name to the frame. 894 * @param identToAdd the name we're adding. 895 */ 896 private void addIdent(DetailAST identToAdd) { 897 varIdents.add(identToAdd); 898 } 899 900 protected AbstractFrame getParent() { 901 return parent; 902 } 903 904 protected String getFrameName() { 905 return frameNameIdent.getText(); 906 } 907 908 public DetailAST getFrameNameIdent() { 909 return frameNameIdent; 910 } 911 912 /** 913 * Check whether the frame contains a field or a variable with the given name. 914 * @param nameToFind the IDENT ast of the name we're looking for. 915 * @return whether it was found. 916 */ 917 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 918 return containsFieldOrVariableDef(varIdents, nameToFind); 919 } 920 921 /** 922 * Check whether the frame contains a given name. 923 * @param nameToFind IDENT ast of the name we're looking for. 924 * @param lookForMethod whether we are looking for a method name. 925 * @return whether it was found. 926 */ 927 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 928 final AbstractFrame frame; 929 930 if (!lookForMethod 931 && containsFieldOrVariable(nameToFind)) { 932 frame = this; 933 } 934 else { 935 frame = parent.getIfContains(nameToFind, lookForMethod); 936 } 937 return frame; 938 } 939 940 /** 941 * Whether the set contains a declaration with the text of the specified 942 * IDENT ast and it is declared in a proper position. 943 * @param set the set of declarations. 944 * @param ident the specified IDENT ast. 945 * @return true if the set contains a declaration with the text of the specified 946 * IDENT ast and it is declared in a proper position. 947 */ 948 protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) { 949 boolean result = false; 950 for (DetailAST ast: set) { 951 if (isProperDefinition(ident, ast)) { 952 result = true; 953 break; 954 } 955 } 956 return result; 957 } 958 959 /** 960 * Whether the definition is correspondent to the IDENT. 961 * @param ident the IDENT ast to check. 962 * @param ast the IDENT ast of the definition to check. 963 * @return true if ast is correspondent to ident. 964 */ 965 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 966 final String nameToFind = ident.getText(); 967 return nameToFind.equals(ast.getText()) 968 && checkPosition(ast, ident); 969 } 970 971 /** 972 * Whether the declaration is located before the checked ast. 973 * @param ast1 the IDENT ast of the declaration. 974 * @param ast2 the IDENT ast to check. 975 * @return true, if the declaration is located before the checked ast. 976 */ 977 private static boolean checkPosition(DetailAST ast1, DetailAST ast2) { 978 boolean result = false; 979 if (ast1.getLineNo() < ast2.getLineNo() 980 || ast1.getLineNo() == ast2.getLineNo() 981 && ast1.getColumnNo() < ast2.getColumnNo()) { 982 result = true; 983 } 984 return result; 985 } 986 } 987 988 /** 989 * A frame initiated at method definition; holds a method definition token. 990 * @author Stephen Bloch 991 * @author Andrei Selkin 992 */ 993 private static class MethodFrame extends AbstractFrame { 994 995 /** 996 * Creates method frame. 997 * @param parent parent frame. 998 * @param ident method name identifier token. 999 */ 1000 protected MethodFrame(AbstractFrame parent, DetailAST ident) { 1001 super(parent, ident); 1002 } 1003 1004 @Override 1005 protected FrameType getType() { 1006 return FrameType.METHOD_FRAME; 1007 } 1008 } 1009 1010 /** 1011 * A frame initiated at constructor definition. 1012 * @author Andrei Selkin 1013 */ 1014 private static class ConstructorFrame extends AbstractFrame { 1015 1016 /** 1017 * Creates a constructor frame. 1018 * @param parent parent frame. 1019 * @param ident frame name ident. 1020 */ 1021 protected ConstructorFrame(AbstractFrame parent, DetailAST ident) { 1022 super(parent, ident); 1023 } 1024 1025 @Override 1026 protected FrameType getType() { 1027 return FrameType.CTOR_FRAME; 1028 } 1029 } 1030 1031 /** 1032 * A frame initiated at class< enum or interface definition; holds instance variable names. 1033 * @author Stephen Bloch 1034 * @author Andrei Selkin 1035 */ 1036 private static class ClassFrame extends AbstractFrame { 1037 /** Set of idents of instance members declared in this frame. */ 1038 private final Set<DetailAST> instanceMembers; 1039 /** Set of idents of instance methods declared in this frame. */ 1040 private final Set<DetailAST> instanceMethods; 1041 /** Set of idents of variables declared in this frame. */ 1042 private final Set<DetailAST> staticMembers; 1043 /** Set of idents of static methods declared in this frame. */ 1044 private final Set<DetailAST> staticMethods; 1045 1046 /** 1047 * Creates new instance of ClassFrame. 1048 * @param parent parent frame. 1049 * @param ident frame name ident. 1050 */ 1051 ClassFrame(AbstractFrame parent, DetailAST ident) { 1052 super(parent, ident); 1053 instanceMembers = Sets.newHashSet(); 1054 instanceMethods = Sets.newHashSet(); 1055 staticMembers = Sets.newHashSet(); 1056 staticMethods = Sets.newHashSet(); 1057 } 1058 1059 @Override 1060 protected FrameType getType() { 1061 return FrameType.CLASS_FRAME; 1062 } 1063 1064 /** 1065 * Adds static member's ident. 1066 * @param ident an ident of static member of the class. 1067 */ 1068 public void addStaticMember(final DetailAST ident) { 1069 staticMembers.add(ident); 1070 } 1071 1072 /** 1073 * Adds static method's name. 1074 * @param ident an ident of static method of the class. 1075 */ 1076 public void addStaticMethod(final DetailAST ident) { 1077 staticMethods.add(ident); 1078 } 1079 1080 /** 1081 * Adds instance member's ident. 1082 * @param ident an ident of instance member of the class. 1083 */ 1084 public void addInstanceMember(final DetailAST ident) { 1085 instanceMembers.add(ident); 1086 } 1087 1088 /** 1089 * Adds instance method's name. 1090 * @param ident an ident of instance method of the class. 1091 */ 1092 public void addInstanceMethod(final DetailAST ident) { 1093 instanceMethods.add(ident); 1094 } 1095 1096 /** 1097 * Checks if a given name is a known instance member of the class. 1098 * @param ident the IDENT ast of the name to check. 1099 * @return true is the given name is a name of a known 1100 * instance member of the class. 1101 */ 1102 public boolean hasInstanceMember(final DetailAST ident) { 1103 return containsFieldOrVariableDef(instanceMembers, ident); 1104 } 1105 1106 /** 1107 * Checks if a given name is a known instance method of the class. 1108 * @param ident the IDENT ast of the method call to check. 1109 * @return true if the given ast is correspondent to a known 1110 * instance method of the class. 1111 */ 1112 public boolean hasInstanceMethod(final DetailAST ident) { 1113 return containsMethodDef(instanceMethods, ident); 1114 } 1115 1116 /** 1117 * Checks if a given name is a known static method of the class. 1118 * @param ident the IDENT ast of the method call to check. 1119 * @return true is the given ast is correspondent to a known 1120 * instance method of the class. 1121 */ 1122 public boolean hasStaticMethod(final DetailAST ident) { 1123 return containsMethodDef(staticMethods, ident); 1124 } 1125 1126 /** 1127 * Checks whether given instance member has final modifier. 1128 * @param instanceMember an instance member of a class. 1129 * @return true if given instance member has final modifier. 1130 */ 1131 public boolean hasFinalField(final DetailAST instanceMember) { 1132 boolean result = false; 1133 for (DetailAST member : instanceMembers) { 1134 final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS); 1135 final boolean finalMod = mods.branchContains(TokenTypes.FINAL); 1136 if (finalMod && member.equals(instanceMember)) { 1137 result = true; 1138 } 1139 } 1140 return result; 1141 } 1142 1143 @Override 1144 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 1145 return containsFieldOrVariableDef(instanceMembers, nameToFind) 1146 || containsFieldOrVariableDef(staticMembers, nameToFind); 1147 } 1148 1149 @Override 1150 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1151 final String nameToFind = ident.getText(); 1152 return nameToFind.equals(ast.getText()); 1153 } 1154 1155 @Override 1156 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 1157 AbstractFrame frame = null; 1158 1159 if (lookForMethod && containsMethod(nameToFind) 1160 || containsFieldOrVariable(nameToFind)) { 1161 frame = this; 1162 } 1163 else if (getParent() != null) { 1164 frame = getParent().getIfContains(nameToFind, lookForMethod); 1165 } 1166 return frame; 1167 } 1168 1169 /** 1170 * Check whether the frame contains a given method. 1171 * @param methodToFind the AST of the method to find. 1172 * @return true, if a method with the same name and number of parameters is found. 1173 */ 1174 private boolean containsMethod(DetailAST methodToFind) { 1175 return containsMethodDef(instanceMethods, methodToFind) 1176 || containsMethodDef(staticMethods, methodToFind); 1177 } 1178 1179 /** 1180 * Whether the set contains a method definition with the 1181 * same name and number of parameters. 1182 * @param set the set of definitions. 1183 * @param ident the specified method call IDENT ast. 1184 * @return true if the set contains a definition with the 1185 * same name and number of parameters. 1186 */ 1187 private boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) { 1188 boolean result = false; 1189 for (DetailAST ast: set) { 1190 if (isSimilarSignature(ident, ast)) { 1191 result = true; 1192 break; 1193 } 1194 } 1195 return result; 1196 } 1197 1198 /** 1199 * Whether the method definition has the same name and number of parameters. 1200 * @param ident the specified method call IDENT ast. 1201 * @param ast the ast of a method definition to compare with. 1202 * @return true if a method definition has the same name and number of parameters 1203 * as the method call. 1204 */ 1205 private boolean isSimilarSignature(DetailAST ident, DetailAST ast) { 1206 boolean result = false; 1207 final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST); 1208 if (elistToken != null && ident.getText().equals(ast.getText())) { 1209 final int paramsNumber = 1210 ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount(); 1211 final int argsNumber = elistToken.getChildCount(); 1212 result = paramsNumber == argsNumber; 1213 } 1214 return result; 1215 } 1216 } 1217 1218 /** 1219 * A frame initiated on entering a statement list; holds local variable names. 1220 * @author Stephen Bloch 1221 */ 1222 private static class BlockFrame extends AbstractFrame { 1223 1224 /** 1225 * Creates block frame. 1226 * @param parent parent frame. 1227 * @param ident ident frame name ident. 1228 */ 1229 protected BlockFrame(AbstractFrame parent, DetailAST ident) { 1230 super(parent, ident); 1231 } 1232 1233 @Override 1234 protected FrameType getType() { 1235 return FrameType.BLOCK_FRAME; 1236 } 1237 } 1238}