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