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