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