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 final AbstractFrame fieldFrame = findClassFrame(ast, false); 309 310 if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) { 311 frame = getClassFrameWhereViolationIsFound(ast); 312 } 313 } 314 return frame; 315 } 316 317 /** 318 * Parses the next AST for declarations. 319 * @param frameStack stack containing the FrameTree being built. 320 * @param ast AST to parse. 321 */ 322 private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) { 323 final AbstractFrame frame = frameStack.peek(); 324 switch (ast.getType()) { 325 case TokenTypes.VARIABLE_DEF : 326 collectVariableDeclarations(ast, frame); 327 break; 328 case TokenTypes.PARAMETER_DEF : 329 if (!CheckUtils.isReceiverParameter(ast)) { 330 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT); 331 frame.addIdent(parameterIdent); 332 } 333 break; 334 case TokenTypes.CLASS_DEF : 335 case TokenTypes.INTERFACE_DEF : 336 case TokenTypes.ENUM_DEF : 337 case TokenTypes.ANNOTATION_DEF : 338 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 339 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent)); 340 break; 341 case TokenTypes.SLIST : 342 frameStack.addFirst(new BlockFrame(frame, ast)); 343 break; 344 case TokenTypes.METHOD_DEF : 345 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 346 if (frame.getType() == FrameType.CLASS_FRAME) { 347 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS); 348 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 349 ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent); 350 } 351 else { 352 ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent); 353 } 354 } 355 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent)); 356 break; 357 case TokenTypes.CTOR_DEF : 358 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 359 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent)); 360 break; 361 default: 362 // do nothing 363 } 364 } 365 366 /** 367 * Collects variable declarations. 368 * @param ast variable token. 369 * @param frame current frame. 370 */ 371 private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) { 372 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 373 if (frame.getType() == FrameType.CLASS_FRAME) { 374 final DetailAST mods = 375 ast.findFirstToken(TokenTypes.MODIFIERS); 376 if (ScopeUtils.isInInterfaceBlock(ast) 377 || mods.branchContains(TokenTypes.LITERAL_STATIC)) { 378 ((ClassFrame) frame).addStaticMember(ident); 379 } 380 else { 381 ((ClassFrame) frame).addInstanceMember(ident); 382 } 383 } 384 else { 385 frame.addIdent(ident); 386 } 387 } 388 389 /** 390 * Ends parsing of the AST for declarations. 391 * @param frameStack Stack containing the FrameTree being built. 392 * @param ast AST that was parsed. 393 */ 394 private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) { 395 switch (ast.getType()) { 396 case TokenTypes.CLASS_DEF : 397 case TokenTypes.INTERFACE_DEF : 398 case TokenTypes.ENUM_DEF : 399 case TokenTypes.ANNOTATION_DEF : 400 case TokenTypes.SLIST : 401 case TokenTypes.METHOD_DEF : 402 case TokenTypes.CTOR_DEF : 403 frames.put(ast, frameStack.poll()); 404 break; 405 default : 406 // do nothing 407 } 408 } 409 410 /** 411 * Returns the class frame where violation is found (where the field is used without 'this') 412 * or null otherwise. 413 * @param ast IDENT ast to check. 414 * @return the class frame where violation is found or null otherwise. 415 */ 416 private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) { 417 AbstractFrame frameWhereViolationIsFound = null; 418 final AbstractFrame variableDeclarationFrame = findFrame(ast, false); 419 final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType(); 420 final DetailAST prevSibling = ast.getPreviousSibling(); 421 if (variableDeclarationFrameType == FrameType.CLASS_FRAME 422 && !validateOnlyOverlapping 423 && prevSibling == null 424 && canBeReferencedFromStaticContext(ast)) { 425 frameWhereViolationIsFound = variableDeclarationFrame; 426 } 427 else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) { 428 if (isOverlappingByArgument(ast)) { 429 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 430 && !isReturnedVariable(variableDeclarationFrame, ast) 431 && canBeReferencedFromStaticContext(ast) 432 && canAssignValueToClassField(ast)) { 433 frameWhereViolationIsFound = findFrame(ast, true); 434 } 435 } 436 else if (!validateOnlyOverlapping 437 && prevSibling == null 438 && isAssignToken(ast.getParent().getType()) 439 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 440 && canBeReferencedFromStaticContext(ast) 441 && canAssignValueToClassField(ast)) { 442 frameWhereViolationIsFound = findFrame(ast, true); 443 444 } 445 } 446 else if (variableDeclarationFrameType == FrameType.CTOR_FRAME 447 && isOverlappingByArgument(ast) 448 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) { 449 frameWhereViolationIsFound = findFrame(ast, true); 450 } 451 else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME) { 452 if (isOverlappingByLocalVariable(ast)) { 453 if (canAssignValueToClassField(ast) 454 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 455 && !isReturnedVariable(variableDeclarationFrame, ast) 456 && canBeReferencedFromStaticContext(ast)) { 457 frameWhereViolationIsFound = findFrame(ast, true); 458 } 459 } 460 else if (!validateOnlyOverlapping 461 && prevSibling == null 462 && isAssignToken(ast.getParent().getType()) 463 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 464 && canBeReferencedFromStaticContext(ast)) { 465 frameWhereViolationIsFound = findFrame(ast, true); 466 } 467 } 468 return frameWhereViolationIsFound; 469 } 470 471 /** 472 * Checks whether user arranges 'this' for variable in method, constructor, or block on his own. 473 * @param currentFrame current frame. 474 * @param ident ident token. 475 * @return true if user arranges 'this' for variable in method, constructor, 476 * or block on his own. 477 */ 478 private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame, 479 DetailAST ident) { 480 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 481 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 482 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 483 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 484 485 boolean userDefinedArrangementOfThis = false; 486 487 final Set<DetailAST> variableUsagesInsideBlock = 488 getAllTokensWhichAreEqualToCurrent(definitionToken, ident, 489 blockEndToken.getLineNo()); 490 491 for (DetailAST variableUsage : variableUsagesInsideBlock) { 492 final DetailAST prevSibling = variableUsage.getPreviousSibling(); 493 if (prevSibling != null 494 && prevSibling.getType() == TokenTypes.LITERAL_THIS) { 495 userDefinedArrangementOfThis = true; 496 } 497 } 498 return userDefinedArrangementOfThis; 499 } 500 501 /** 502 * Returns the token which ends the code block. 503 * @param blockNameIdent block name identifier. 504 * @param blockStartToken token which starts the block. 505 * @return the token which ends the code block. 506 */ 507 private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) { 508 final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent, TokenTypes.RCURLY); 509 DetailAST blockEndToken = null; 510 for (DetailAST currentRcurly : rcurlyTokens) { 511 final DetailAST parent = currentRcurly.getParent(); 512 if (blockStartToken.getLineNo() == parent.getLineNo()) { 513 blockEndToken = currentRcurly; 514 } 515 } 516 return blockEndToken; 517 } 518 519 /** 520 * Checks whether the current variable is returned from the method. 521 * @param currentFrame current frame. 522 * @param ident variable ident token. 523 * @return true if the current variable is returned from the method. 524 */ 525 private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) { 526 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 527 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 528 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 529 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 530 531 final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken, 532 TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo()); 533 534 boolean returnedVariable = false; 535 for (DetailAST returnToken : returnsInsideBlock) { 536 returnedVariable = returnToken.findAll(ident).hasMoreNodes(); 537 if (returnedVariable) { 538 break; 539 } 540 } 541 return returnedVariable; 542 } 543 544 /** 545 * Checks whether a field can be referenced from a static context. 546 * @param ident ident token. 547 * @return true if field can be referenced from a static context. 548 */ 549 private boolean canBeReferencedFromStaticContext(DetailAST ident) { 550 AbstractFrame variableDeclarationFrame = findFrame(ident, false); 551 boolean staticInitializationBlock = false; 552 while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME) { 553 final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 554 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 555 if (definitionToken.getType() == TokenTypes.STATIC_INIT) { 556 staticInitializationBlock = true; 557 break; 558 } 559 variableDeclarationFrame = variableDeclarationFrame.getParent(); 560 } 561 562 boolean staticContext = false; 563 if (staticInitializationBlock) { 564 staticContext = true; 565 } 566 else { 567 if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) { 568 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident); 569 if (codeBlockDefinition != null) { 570 final DetailAST modifiers = codeBlockDefinition.getFirstChild(); 571 staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT 572 || modifiers.branchContains(TokenTypes.LITERAL_STATIC); 573 } 574 } 575 else { 576 final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 577 final DetailAST definitionToken = frameNameIdent.getParent(); 578 staticContext = definitionToken.branchContains(TokenTypes.LITERAL_STATIC); 579 } 580 } 581 return !staticContext; 582 } 583 584 /** 585 * Returns code block definition token for current identifier. 586 * @param ident ident token. 587 * @return code block definition token for current identifier or null if code block 588 * definition was not found. 589 */ 590 private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) { 591 DetailAST parent = ident.getParent(); 592 while (parent != null 593 && parent.getType() != TokenTypes.METHOD_DEF 594 && parent.getType() != TokenTypes.CTOR_DEF 595 && parent.getType() != TokenTypes.STATIC_INIT) { 596 parent = parent.getParent(); 597 } 598 return parent; 599 } 600 601 /** 602 * Checks whether a value can be assigned to a field. 603 * A value can be assigned to a final field only in constructor block. If there is a method 604 * block, value assignment can be performed only to non final field. 605 * @param ast an identifier token. 606 * @return true if a value can be assigned to a field. 607 */ 608 private boolean canAssignValueToClassField(DetailAST ast) { 609 final AbstractFrame fieldUsageFrame = findFrame(ast, false); 610 final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame); 611 612 final AbstractFrame declarationFrame = findFrame(ast, true); 613 final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast); 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 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 649 if (isCompoundAssignToken(parent.getType())) { 650 overlapping = true; 651 } 652 else { 653 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 654 } 655 } 656 return overlapping; 657 } 658 659 /** 660 * Checks whether an overlapping by local variable takes place. 661 * @param ast an identifier. 662 * @return true if an overlapping by local variable takes place. 663 */ 664 private boolean isOverlappingByLocalVariable(DetailAST ast) { 665 boolean overlapping = false; 666 final DetailAST parent = ast.getParent(); 667 final DetailAST sibling = ast.getNextSibling(); 668 if (sibling != null && isAssignToken(parent.getType())) { 669 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true); 670 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 671 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 672 } 673 return overlapping; 674 } 675 676 /** 677 * Collects all tokens of specific type starting with the current ast node. 678 * @param ast ast node. 679 * @param tokenType token type. 680 * @return a set of all tokens of specific type starting with the current ast node. 681 */ 682 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 683 DetailAST vertex = ast; 684 final Set<DetailAST> result = Sets.newHashSet(); 685 final Deque<DetailAST> stack = Queues.newArrayDeque(); 686 while (vertex != null || !stack.isEmpty()) { 687 if (!stack.isEmpty()) { 688 vertex = stack.pop(); 689 } 690 while (vertex != null) { 691 if (vertex.getType() == tokenType) { 692 result.add(vertex); 693 } 694 if (vertex.getNextSibling() != null) { 695 stack.push(vertex.getNextSibling()); 696 } 697 vertex = vertex.getFirstChild(); 698 } 699 } 700 return result; 701 } 702 703 /** 704 * Collects all tokens of specific type starting with the current ast node and which line 705 * number is lower or equal to the end line number. 706 * @param ast ast node. 707 * @param tokenType token type. 708 * @param endLineNumber end line number. 709 * @return a set of all tokens of specific type starting with the current ast node and which 710 * line number is lower or equal to the end line number. 711 */ 712 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType, 713 int endLineNumber) { 714 DetailAST vertex = ast; 715 final Set<DetailAST> result = Sets.newHashSet(); 716 final Deque<DetailAST> stack = Queues.newArrayDeque(); 717 while (vertex != null || !stack.isEmpty()) { 718 if (!stack.isEmpty()) { 719 vertex = stack.pop(); 720 } 721 while (vertex != null) { 722 if (tokenType == vertex.getType() 723 && vertex.getLineNo() <= endLineNumber) { 724 result.add(vertex); 725 } 726 if (vertex.getNextSibling() != null) { 727 stack.push(vertex.getNextSibling()); 728 } 729 vertex = vertex.getFirstChild(); 730 } 731 } 732 return result; 733 } 734 735 /** 736 * Collects all tokens which are equal to current token starting with the current ast node and 737 * which line number is lower or equal to the end line number. 738 * @param ast ast node. 739 * @param token token. 740 * @param endLineNumber end line number. 741 * @return a set of tokens which are equal to current token starting with the current ast node 742 * and which line number is lower or equal to the end line number. 743 */ 744 private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token, 745 int endLineNumber) { 746 DetailAST vertex = ast; 747 final Set<DetailAST> result = Sets.newHashSet(); 748 final Deque<DetailAST> stack = Queues.newArrayDeque(); 749 while (vertex != null || !stack.isEmpty()) { 750 if (!stack.isEmpty()) { 751 vertex = stack.pop(); 752 } 753 while (vertex != null) { 754 if (token.equals(vertex) 755 && vertex.getLineNo() <= endLineNumber) { 756 result.add(vertex); 757 } 758 if (vertex.getNextSibling() != null) { 759 stack.push(vertex.getNextSibling()); 760 } 761 vertex = vertex.getFirstChild(); 762 } 763 } 764 return result; 765 } 766 767 /** 768 * Returns the frame where the method is declared, if the given method is used without 769 * 'this' and null otherwise. 770 * @param ast the IDENT ast of the name to check. 771 * @return the frame where the method is declared, if the given method is used without 772 * 'this' and null otherwise. 773 */ 774 private AbstractFrame getMethodWithoutThis(DetailAST ast) { 775 AbstractFrame result = null; 776 final AbstractFrame frame = findFrame(ast, true); 777 if (frame != null 778 && !validateOnlyOverlapping 779 && ((ClassFrame) frame).hasInstanceMethod(ast) 780 && !((ClassFrame) frame).hasStaticMethod(ast)) { 781 result = frame; 782 } 783 return result; 784 } 785 786 /** 787 * Find the class frame containing declaration. 788 * @param name IDENT ast of the declaration to find. 789 * @param lookForMethod whether we are looking for a method name. 790 * @return AbstractFrame containing declaration or null. 791 */ 792 private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) { 793 AbstractFrame frame = current; 794 795 while (true) { 796 frame = findFrame(frame, name, lookForMethod); 797 798 if (frame == null || frame instanceof ClassFrame) { 799 break; 800 } 801 802 frame = frame.getParent(); 803 } 804 805 return frame; 806 } 807 808 /** 809 * Find frame containing declaration. 810 * @param name IDENT ast of the declaration to find. 811 * @param lookForMethod whether we are looking for a method name. 812 * @return AbstractFrame containing declaration or null. 813 */ 814 private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) { 815 return findFrame(current, name, lookForMethod); 816 } 817 818 /** 819 * Find frame containing declaration. 820 * @param frame The parent frame to searching in. 821 * @param name IDENT ast of the declaration to find. 822 * @param lookForMethod whether we are looking for a method name. 823 * @return AbstractFrame containing declaration or null. 824 */ 825 private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name, 826 boolean lookForMethod) { 827 final AbstractFrame result; 828 if (frame == null) { 829 result = null; 830 } 831 else { 832 result = frame.getIfContains(name, lookForMethod); 833 } 834 return result; 835 } 836 837 /** 838 * Check that token is related to Definition tokens. 839 * @param parentType token Type. 840 * @return true if token is related to Definition Tokens. 841 */ 842 private static boolean isDeclarationToken(int parentType) { 843 return DECLARATION_TOKENS.contains(parentType); 844 } 845 846 /** 847 * Check that token is related to assign tokens. 848 * @param tokenType token type. 849 * @return true if token is related to assign tokens. 850 */ 851 private static boolean isAssignToken(int tokenType) { 852 return ASSIGN_TOKENS.contains(tokenType); 853 } 854 855 /** 856 * Check that token is related to compound assign tokens. 857 * @param tokenType token type. 858 * @return true if token is related to compound assign tokens. 859 */ 860 private static boolean isCompoundAssignToken(int tokenType) { 861 return COMPOUND_ASSIGN_TOKENS.contains(tokenType); 862 } 863 864 /** 865 * Gets the name of the nearest parent ClassFrame. 866 * @return the name of the nearest parent ClassFrame. 867 */ 868 private String getNearestClassFrameName() { 869 AbstractFrame frame = current; 870 while (frame.getType() != FrameType.CLASS_FRAME) { 871 frame = frame.getParent(); 872 } 873 return frame.getFrameName(); 874 } 875 876 /** An AbstractFrame type. */ 877 private enum FrameType { 878 /** Class frame type. */ 879 CLASS_FRAME, 880 /** Constructor frame type. */ 881 CTOR_FRAME, 882 /** Method frame type. */ 883 METHOD_FRAME, 884 /** Block frame type. */ 885 BLOCK_FRAME, 886 } 887 888 /** 889 * A declaration frame. 890 * @author Stephen Bloch 891 * @author Andrei Selkin 892 */ 893 private abstract static class AbstractFrame { 894 /** Set of name of variables declared in this frame. */ 895 private final Set<DetailAST> varIdents; 896 897 /** Parent frame. */ 898 private final AbstractFrame parent; 899 900 /** Name identifier token. */ 901 private final DetailAST frameNameIdent; 902 903 /** 904 * Constructor -- invokable only via super() from subclasses. 905 * @param parent parent frame. 906 * @param ident frame name ident. 907 */ 908 protected AbstractFrame(AbstractFrame parent, DetailAST ident) { 909 this.parent = parent; 910 frameNameIdent = ident; 911 varIdents = Sets.newHashSet(); 912 } 913 914 /** 915 * Get the type of the frame. 916 * @return a FrameType. 917 */ 918 protected abstract FrameType getType(); 919 920 /** 921 * Add a name to the frame. 922 * @param identToAdd the name we're adding. 923 */ 924 private void addIdent(DetailAST identToAdd) { 925 varIdents.add(identToAdd); 926 } 927 928 protected AbstractFrame getParent() { 929 return parent; 930 } 931 932 protected String getFrameName() { 933 return frameNameIdent.getText(); 934 } 935 936 public DetailAST getFrameNameIdent() { 937 return frameNameIdent; 938 } 939 940 /** 941 * Check whether the frame contains a field or a variable with the given name. 942 * @param nameToFind the IDENT ast of the name we're looking for. 943 * @return whether it was found. 944 */ 945 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 946 return containsFieldOrVariableDef(varIdents, nameToFind); 947 } 948 949 /** 950 * Check whether the frame contains a given name. 951 * @param nameToFind IDENT ast of the name we're looking for. 952 * @param lookForMethod whether we are looking for a method name. 953 * @return whether it was found. 954 */ 955 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 956 final AbstractFrame frame; 957 958 if (!lookForMethod 959 && containsFieldOrVariable(nameToFind)) { 960 frame = this; 961 } 962 else { 963 frame = parent.getIfContains(nameToFind, lookForMethod); 964 } 965 return frame; 966 } 967 968 /** 969 * Whether the set contains a declaration with the text of the specified 970 * IDENT ast and it is declared in a proper position. 971 * @param set the set of declarations. 972 * @param ident the specified IDENT ast. 973 * @return true if the set contains a declaration with the text of the specified 974 * IDENT ast and it is declared in a proper position. 975 */ 976 protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) { 977 boolean result = false; 978 for (DetailAST ast: set) { 979 if (isProperDefinition(ident, ast)) { 980 result = true; 981 break; 982 } 983 } 984 return result; 985 } 986 987 /** 988 * Whether the definition is correspondent to the IDENT. 989 * @param ident the IDENT ast to check. 990 * @param ast the IDENT ast of the definition to check. 991 * @return true if ast is correspondent to ident. 992 */ 993 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 994 final String nameToFind = ident.getText(); 995 return nameToFind.equals(ast.getText()) 996 && checkPosition(ast, ident); 997 } 998 999 /** 1000 * Whether the declaration is located before the checked ast. 1001 * @param ast1 the IDENT ast of the declaration. 1002 * @param ast2 the IDENT ast to check. 1003 * @return true, if the declaration is located before the checked ast. 1004 */ 1005 private static boolean checkPosition(DetailAST ast1, DetailAST ast2) { 1006 boolean result = false; 1007 if (ast1.getLineNo() < ast2.getLineNo() 1008 || ast1.getLineNo() == ast2.getLineNo() 1009 && ast1.getColumnNo() < ast2.getColumnNo()) { 1010 result = true; 1011 } 1012 return result; 1013 } 1014 } 1015 1016 /** 1017 * A frame initiated at method definition; holds a method definition token. 1018 * @author Stephen Bloch 1019 * @author Andrei Selkin 1020 */ 1021 private static class MethodFrame extends AbstractFrame { 1022 1023 /** 1024 * Creates method frame. 1025 * @param parent parent frame. 1026 * @param ident method name identifier token. 1027 */ 1028 protected MethodFrame(AbstractFrame parent, DetailAST ident) { 1029 super(parent, ident); 1030 } 1031 1032 @Override 1033 protected FrameType getType() { 1034 return FrameType.METHOD_FRAME; 1035 } 1036 } 1037 1038 /** 1039 * A frame initiated at constructor definition. 1040 * @author Andrei Selkin 1041 */ 1042 private static class ConstructorFrame extends AbstractFrame { 1043 1044 /** 1045 * Creates a constructor frame. 1046 * @param parent parent frame. 1047 * @param ident frame name ident. 1048 */ 1049 protected ConstructorFrame(AbstractFrame parent, DetailAST ident) { 1050 super(parent, ident); 1051 } 1052 1053 @Override 1054 protected FrameType getType() { 1055 return FrameType.CTOR_FRAME; 1056 } 1057 } 1058 1059 /** 1060 * A frame initiated at class< enum or interface definition; holds instance variable names. 1061 * @author Stephen Bloch 1062 * @author Andrei Selkin 1063 */ 1064 private static class ClassFrame extends AbstractFrame { 1065 /** Set of idents of instance members declared in this frame. */ 1066 private final Set<DetailAST> instanceMembers; 1067 /** Set of idents of instance methods declared in this frame. */ 1068 private final Set<DetailAST> instanceMethods; 1069 /** Set of idents of variables declared in this frame. */ 1070 private final Set<DetailAST> staticMembers; 1071 /** Set of idents of static methods declared in this frame. */ 1072 private final Set<DetailAST> staticMethods; 1073 1074 /** 1075 * Creates new instance of ClassFrame. 1076 * @param parent parent frame. 1077 * @param ident frame name ident. 1078 */ 1079 ClassFrame(AbstractFrame parent, DetailAST ident) { 1080 super(parent, ident); 1081 instanceMembers = Sets.newHashSet(); 1082 instanceMethods = Sets.newHashSet(); 1083 staticMembers = Sets.newHashSet(); 1084 staticMethods = Sets.newHashSet(); 1085 } 1086 1087 @Override 1088 protected FrameType getType() { 1089 return FrameType.CLASS_FRAME; 1090 } 1091 1092 /** 1093 * Adds static member's ident. 1094 * @param ident an ident of static member of the class. 1095 */ 1096 public void addStaticMember(final DetailAST ident) { 1097 staticMembers.add(ident); 1098 } 1099 1100 /** 1101 * Adds static method's name. 1102 * @param ident an ident of static method of the class. 1103 */ 1104 public void addStaticMethod(final DetailAST ident) { 1105 staticMethods.add(ident); 1106 } 1107 1108 /** 1109 * Adds instance member's ident. 1110 * @param ident an ident of instance member of the class. 1111 */ 1112 public void addInstanceMember(final DetailAST ident) { 1113 instanceMembers.add(ident); 1114 } 1115 1116 /** 1117 * Adds instance method's name. 1118 * @param ident an ident of instance method of the class. 1119 */ 1120 public void addInstanceMethod(final DetailAST ident) { 1121 instanceMethods.add(ident); 1122 } 1123 1124 /** 1125 * Checks if a given name is a known instance member of the class. 1126 * @param ident the IDENT ast of the name to check. 1127 * @return true is the given name is a name of a known 1128 * instance member of the class. 1129 */ 1130 public boolean hasInstanceMember(final DetailAST ident) { 1131 return containsFieldOrVariableDef(instanceMembers, ident); 1132 } 1133 1134 /** 1135 * Checks if a given name is a known instance method of the class. 1136 * @param ident the IDENT ast of the method call to check. 1137 * @return true if the given ast is correspondent to a known 1138 * instance method of the class. 1139 */ 1140 public boolean hasInstanceMethod(final DetailAST ident) { 1141 return containsMethodDef(instanceMethods, ident); 1142 } 1143 1144 /** 1145 * Checks if a given name is a known static method of the class. 1146 * @param ident the IDENT ast of the method call to check. 1147 * @return true is the given ast is correspondent to a known 1148 * instance method of the class. 1149 */ 1150 public boolean hasStaticMethod(final DetailAST ident) { 1151 return containsMethodDef(staticMethods, ident); 1152 } 1153 1154 /** 1155 * Checks whether given instance member has final modifier. 1156 * @param instanceMember an instance member of a class. 1157 * @return true if given instance member has final modifier. 1158 */ 1159 public boolean hasFinalField(final DetailAST instanceMember) { 1160 boolean result = false; 1161 for (DetailAST member : instanceMembers) { 1162 final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS); 1163 final boolean finalMod = mods.branchContains(TokenTypes.FINAL); 1164 if (finalMod && member.equals(instanceMember)) { 1165 result = true; 1166 } 1167 } 1168 return result; 1169 } 1170 1171 @Override 1172 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 1173 return containsFieldOrVariableDef(instanceMembers, nameToFind) 1174 || containsFieldOrVariableDef(staticMembers, nameToFind); 1175 } 1176 1177 @Override 1178 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1179 final String nameToFind = ident.getText(); 1180 return nameToFind.equals(ast.getText()); 1181 } 1182 1183 @Override 1184 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 1185 AbstractFrame frame = null; 1186 1187 if (lookForMethod && containsMethod(nameToFind) 1188 || containsFieldOrVariable(nameToFind)) { 1189 frame = this; 1190 } 1191 else if (getParent() != null) { 1192 frame = getParent().getIfContains(nameToFind, lookForMethod); 1193 } 1194 return frame; 1195 } 1196 1197 /** 1198 * Check whether the frame contains a given method. 1199 * @param methodToFind the AST of the method to find. 1200 * @return true, if a method with the same name and number of parameters is found. 1201 */ 1202 private boolean containsMethod(DetailAST methodToFind) { 1203 return containsMethodDef(instanceMethods, methodToFind) 1204 || containsMethodDef(staticMethods, methodToFind); 1205 } 1206 1207 /** 1208 * Whether the set contains a method definition with the 1209 * same name and number of parameters. 1210 * @param set the set of definitions. 1211 * @param ident the specified method call IDENT ast. 1212 * @return true if the set contains a definition with the 1213 * same name and number of parameters. 1214 */ 1215 private boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) { 1216 boolean result = false; 1217 for (DetailAST ast: set) { 1218 if (isSimilarSignature(ident, ast)) { 1219 result = true; 1220 break; 1221 } 1222 } 1223 return result; 1224 } 1225 1226 /** 1227 * Whether the method definition has the same name and number of parameters. 1228 * @param ident the specified method call IDENT ast. 1229 * @param ast the ast of a method definition to compare with. 1230 * @return true if a method definition has the same name and number of parameters 1231 * as the method call. 1232 */ 1233 private boolean isSimilarSignature(DetailAST ident, DetailAST ast) { 1234 boolean result = false; 1235 final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST); 1236 if (elistToken != null && ident.getText().equals(ast.getText())) { 1237 final int paramsNumber = 1238 ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount(); 1239 final int argsNumber = elistToken.getChildCount(); 1240 result = paramsNumber == argsNumber; 1241 } 1242 return result; 1243 } 1244 } 1245 1246 /** 1247 * A frame initiated on entering a statement list; holds local variable names. 1248 * @author Stephen Bloch 1249 */ 1250 private static class BlockFrame extends AbstractFrame { 1251 1252 /** 1253 * Creates block frame. 1254 * @param parent parent frame. 1255 * @param ident ident frame name ident. 1256 */ 1257 protected BlockFrame(AbstractFrame parent, DetailAST ident) { 1258 super(parent, ident); 1259 } 1260 1261 @Override 1262 protected FrameType getType() { 1263 return FrameType.BLOCK_FRAME; 1264 } 1265 } 1266}