001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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.CheckUtil; 039import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 040import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 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 = ScopeUtil.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 (!CheckUtil.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 (ScopeUtil.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 */ 489 // -@cs[CyclomaticComplexity] Method already invokes too many methods that fully explain 490 // a logic, additional abstraction will not make logic/algorithm more readable. 491 private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) { 492 AbstractFrame frameWhereViolationIsFound = null; 493 final AbstractFrame variableDeclarationFrame = findFrame(ast, false); 494 final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType(); 495 final DetailAST prevSibling = ast.getPreviousSibling(); 496 if (variableDeclarationFrameType == FrameType.CLASS_FRAME 497 && !validateOnlyOverlapping 498 && prevSibling == null 499 && canBeReferencedFromStaticContext(ast)) { 500 frameWhereViolationIsFound = variableDeclarationFrame; 501 } 502 else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) { 503 if (isOverlappingByArgument(ast)) { 504 if (!isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 505 && !isReturnedVariable(variableDeclarationFrame, ast) 506 && canBeReferencedFromStaticContext(ast) 507 && canAssignValueToClassField(ast)) { 508 frameWhereViolationIsFound = findFrame(ast, true); 509 } 510 } 511 else if (!validateOnlyOverlapping 512 && prevSibling == null 513 && isAssignToken(ast.getParent().getType()) 514 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 515 && canBeReferencedFromStaticContext(ast) 516 && canAssignValueToClassField(ast)) { 517 frameWhereViolationIsFound = findFrame(ast, true); 518 } 519 } 520 else if (variableDeclarationFrameType == FrameType.CTOR_FRAME 521 && isOverlappingByArgument(ast) 522 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) { 523 frameWhereViolationIsFound = findFrame(ast, true); 524 } 525 else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME 526 && isOverlappingByLocalVariable(ast) 527 && canAssignValueToClassField(ast) 528 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 529 && !isReturnedVariable(variableDeclarationFrame, ast) 530 && canBeReferencedFromStaticContext(ast)) { 531 frameWhereViolationIsFound = findFrame(ast, true); 532 } 533 return frameWhereViolationIsFound; 534 } 535 536 /** 537 * Checks whether user arranges 'this' for variable in method, constructor, or block on his own. 538 * @param currentFrame current frame. 539 * @param ident ident token. 540 * @return true if user arranges 'this' for variable in method, constructor, 541 * or block on his own. 542 */ 543 private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame, 544 DetailAST ident) { 545 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 546 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 547 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 548 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 549 550 boolean userDefinedArrangementOfThis = false; 551 552 final Set<DetailAST> variableUsagesInsideBlock = 553 getAllTokensWhichAreEqualToCurrent(definitionToken, ident, 554 blockEndToken.getLineNo()); 555 556 for (DetailAST variableUsage : variableUsagesInsideBlock) { 557 final DetailAST prevSibling = variableUsage.getPreviousSibling(); 558 if (prevSibling != null 559 && prevSibling.getType() == TokenTypes.LITERAL_THIS) { 560 userDefinedArrangementOfThis = true; 561 break; 562 } 563 } 564 return userDefinedArrangementOfThis; 565 } 566 567 /** 568 * Returns the token which ends the code block. 569 * @param blockNameIdent block name identifier. 570 * @param blockStartToken token which starts the block. 571 * @return the token which ends the code block. 572 */ 573 private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) { 574 DetailAST blockEndToken = null; 575 final DetailAST blockNameIdentParent = blockNameIdent.getParent(); 576 if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) { 577 blockEndToken = blockNameIdentParent.getNextSibling(); 578 } 579 else { 580 final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent, 581 TokenTypes.RCURLY); 582 for (DetailAST currentRcurly : rcurlyTokens) { 583 final DetailAST parent = currentRcurly.getParent(); 584 if (blockStartToken.getLineNo() == parent.getLineNo()) { 585 blockEndToken = currentRcurly; 586 } 587 } 588 } 589 return blockEndToken; 590 } 591 592 /** 593 * Checks whether the current variable is returned from the method. 594 * @param currentFrame current frame. 595 * @param ident variable ident token. 596 * @return true if the current variable is returned from the method. 597 */ 598 private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) { 599 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 600 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 601 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 602 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 603 604 final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken, 605 TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo()); 606 607 boolean returnedVariable = false; 608 for (DetailAST returnToken : returnsInsideBlock) { 609 returnedVariable = returnToken.findAll(ident).hasMoreNodes(); 610 if (returnedVariable) { 611 break; 612 } 613 } 614 return returnedVariable; 615 } 616 617 /** 618 * Checks whether a field can be referenced from a static context. 619 * @param ident ident token. 620 * @return true if field can be referenced from a static context. 621 */ 622 private boolean canBeReferencedFromStaticContext(DetailAST ident) { 623 AbstractFrame variableDeclarationFrame = findFrame(ident, false); 624 boolean staticInitializationBlock = false; 625 while (variableDeclarationFrame.getType() == FrameType.BLOCK_FRAME 626 || variableDeclarationFrame.getType() == FrameType.FOR_FRAME) { 627 final DetailAST blockFrameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 628 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 629 if (definitionToken.getType() == TokenTypes.STATIC_INIT) { 630 staticInitializationBlock = true; 631 break; 632 } 633 variableDeclarationFrame = variableDeclarationFrame.getParent(); 634 } 635 636 boolean staticContext = false; 637 if (staticInitializationBlock) { 638 staticContext = true; 639 } 640 else { 641 if (variableDeclarationFrame.getType() == FrameType.CLASS_FRAME) { 642 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident); 643 if (codeBlockDefinition != null) { 644 final DetailAST modifiers = codeBlockDefinition.getFirstChild(); 645 staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT 646 || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null; 647 } 648 } 649 else { 650 final DetailAST frameNameIdent = variableDeclarationFrame.getFrameNameIdent(); 651 final DetailAST definitionToken = frameNameIdent.getParent(); 652 staticContext = definitionToken.findFirstToken(TokenTypes.MODIFIERS) 653 .findFirstToken(TokenTypes.LITERAL_STATIC) != null; 654 } 655 } 656 return !staticContext; 657 } 658 659 /** 660 * Returns code block definition token for current identifier. 661 * @param ident ident token. 662 * @return code block definition token for current identifier or null if code block 663 * definition was not found. 664 */ 665 private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) { 666 DetailAST parent = ident.getParent(); 667 while (parent != null 668 && parent.getType() != TokenTypes.METHOD_DEF 669 && parent.getType() != TokenTypes.CTOR_DEF 670 && parent.getType() != TokenTypes.STATIC_INIT) { 671 parent = parent.getParent(); 672 } 673 return parent; 674 } 675 676 /** 677 * Checks whether a value can be assigned to a field. 678 * A value can be assigned to a final field only in constructor block. If there is a method 679 * block, value assignment can be performed only to non final field. 680 * @param ast an identifier token. 681 * @return true if a value can be assigned to a field. 682 */ 683 private boolean canAssignValueToClassField(DetailAST ast) { 684 final AbstractFrame fieldUsageFrame = findFrame(ast, false); 685 final boolean fieldUsageInConstructor = isInsideConstructorFrame(fieldUsageFrame); 686 687 final AbstractFrame declarationFrame = findFrame(ast, true); 688 final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast); 689 690 return fieldUsageInConstructor || !finalField; 691 } 692 693 /** 694 * Checks whether a field usage frame is inside constructor frame. 695 * @param frame frame, where field is used. 696 * @return true if the field usage frame is inside constructor frame. 697 */ 698 private static boolean isInsideConstructorFrame(AbstractFrame frame) { 699 boolean assignmentInConstructor = false; 700 AbstractFrame fieldUsageFrame = frame; 701 if (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) { 702 while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) { 703 fieldUsageFrame = fieldUsageFrame.getParent(); 704 } 705 if (fieldUsageFrame.getType() == FrameType.CTOR_FRAME) { 706 assignmentInConstructor = true; 707 } 708 } 709 return assignmentInConstructor; 710 } 711 712 /** 713 * Checks whether an overlapping by method or constructor argument takes place. 714 * @param ast an identifier. 715 * @return true if an overlapping by method or constructor argument takes place. 716 */ 717 private boolean isOverlappingByArgument(DetailAST ast) { 718 boolean overlapping = false; 719 final DetailAST parent = ast.getParent(); 720 final DetailAST sibling = ast.getNextSibling(); 721 if (sibling != null && isAssignToken(parent.getType())) { 722 if (isCompoundAssignToken(parent.getType())) { 723 overlapping = true; 724 } 725 else { 726 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true); 727 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 728 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 729 } 730 } 731 return overlapping; 732 } 733 734 /** 735 * Checks whether an overlapping by local variable takes place. 736 * @param ast an identifier. 737 * @return true if an overlapping by local variable takes place. 738 */ 739 private boolean isOverlappingByLocalVariable(DetailAST ast) { 740 boolean overlapping = false; 741 final DetailAST parent = ast.getParent(); 742 final DetailAST sibling = ast.getNextSibling(); 743 if (sibling != null && isAssignToken(parent.getType())) { 744 final ClassFrame classFrame = (ClassFrame) findFrame(ast, true); 745 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 746 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 747 } 748 return overlapping; 749 } 750 751 /** 752 * Collects all tokens of specific type starting with the current ast node. 753 * @param ast ast node. 754 * @param tokenType token type. 755 * @return a set of all tokens of specific type starting with the current ast node. 756 */ 757 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 758 DetailAST vertex = ast; 759 final Set<DetailAST> result = new HashSet<>(); 760 final Deque<DetailAST> stack = new ArrayDeque<>(); 761 while (vertex != null || !stack.isEmpty()) { 762 if (!stack.isEmpty()) { 763 vertex = stack.pop(); 764 } 765 while (vertex != null) { 766 if (vertex.getType() == tokenType) { 767 result.add(vertex); 768 } 769 if (vertex.getNextSibling() != null) { 770 stack.push(vertex.getNextSibling()); 771 } 772 vertex = vertex.getFirstChild(); 773 } 774 } 775 return result; 776 } 777 778 /** 779 * Collects all tokens of specific type starting with the current ast node and which line 780 * number is lower or equal to the end line number. 781 * @param ast ast node. 782 * @param tokenType token type. 783 * @param endLineNumber end line number. 784 * @return a set of all tokens of specific type starting with the current ast node and which 785 * line number is lower or equal to the end line number. 786 */ 787 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType, 788 int endLineNumber) { 789 DetailAST vertex = ast; 790 final Set<DetailAST> result = new HashSet<>(); 791 final Deque<DetailAST> stack = new ArrayDeque<>(); 792 while (vertex != null || !stack.isEmpty()) { 793 if (!stack.isEmpty()) { 794 vertex = stack.pop(); 795 } 796 while (vertex != null) { 797 if (tokenType == vertex.getType() 798 && vertex.getLineNo() <= endLineNumber) { 799 result.add(vertex); 800 } 801 if (vertex.getNextSibling() != null) { 802 stack.push(vertex.getNextSibling()); 803 } 804 vertex = vertex.getFirstChild(); 805 } 806 } 807 return result; 808 } 809 810 /** 811 * Collects all tokens which are equal to current token starting with the current ast node and 812 * which line number is lower or equal to the end line number. 813 * @param ast ast node. 814 * @param token token. 815 * @param endLineNumber end line number. 816 * @return a set of tokens which are equal to current token starting with the current ast node 817 * and which line number is lower or equal to the end line number. 818 */ 819 private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token, 820 int endLineNumber) { 821 DetailAST vertex = ast; 822 final Set<DetailAST> result = new HashSet<>(); 823 final Deque<DetailAST> stack = new ArrayDeque<>(); 824 while (vertex != null || !stack.isEmpty()) { 825 if (!stack.isEmpty()) { 826 vertex = stack.pop(); 827 } 828 while (vertex != null) { 829 if (token.equals(vertex) 830 && vertex.getLineNo() <= endLineNumber) { 831 result.add(vertex); 832 } 833 if (vertex.getNextSibling() != null) { 834 stack.push(vertex.getNextSibling()); 835 } 836 vertex = vertex.getFirstChild(); 837 } 838 } 839 return result; 840 } 841 842 /** 843 * Returns the frame where the method is declared, if the given method is used without 844 * 'this' and null otherwise. 845 * @param ast the IDENT ast of the name to check. 846 * @return the frame where the method is declared, if the given method is used without 847 * 'this' and null otherwise. 848 */ 849 private AbstractFrame getMethodWithoutThis(DetailAST ast) { 850 AbstractFrame result = null; 851 if (!validateOnlyOverlapping) { 852 final AbstractFrame frame = findFrame(ast, true); 853 if (frame != null 854 && ((ClassFrame) frame).hasInstanceMethod(ast) 855 && !((ClassFrame) frame).hasStaticMethod(ast)) { 856 result = frame; 857 } 858 } 859 return result; 860 } 861 862 /** 863 * Find the class frame containing declaration. 864 * @param name IDENT ast of the declaration to find. 865 * @param lookForMethod whether we are looking for a method name. 866 * @return AbstractFrame containing declaration or null. 867 */ 868 private AbstractFrame findClassFrame(DetailAST name, boolean lookForMethod) { 869 AbstractFrame frame = current.peek(); 870 871 while (true) { 872 frame = findFrame(frame, name, lookForMethod); 873 874 if (frame == null || frame instanceof ClassFrame) { 875 break; 876 } 877 878 frame = frame.getParent(); 879 } 880 881 return frame; 882 } 883 884 /** 885 * Find frame containing declaration. 886 * @param name IDENT ast of the declaration to find. 887 * @param lookForMethod whether we are looking for a method name. 888 * @return AbstractFrame containing declaration or null. 889 */ 890 private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) { 891 return findFrame(current.peek(), name, lookForMethod); 892 } 893 894 /** 895 * Find frame containing declaration. 896 * @param frame The parent frame to searching in. 897 * @param name IDENT ast of the declaration to find. 898 * @param lookForMethod whether we are looking for a method name. 899 * @return AbstractFrame containing declaration or null. 900 */ 901 private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name, 902 boolean lookForMethod) { 903 return frame.getIfContains(name, lookForMethod); 904 } 905 906 /** 907 * Check that token is related to Definition tokens. 908 * @param parentType token Type. 909 * @return true if token is related to Definition Tokens. 910 */ 911 private static boolean isDeclarationToken(int parentType) { 912 return DECLARATION_TOKENS.contains(parentType); 913 } 914 915 /** 916 * Check that token is related to assign tokens. 917 * @param tokenType token type. 918 * @return true if token is related to assign tokens. 919 */ 920 private static boolean isAssignToken(int tokenType) { 921 return ASSIGN_TOKENS.contains(tokenType); 922 } 923 924 /** 925 * Check that token is related to compound assign tokens. 926 * @param tokenType token type. 927 * @return true if token is related to compound assign tokens. 928 */ 929 private static boolean isCompoundAssignToken(int tokenType) { 930 return COMPOUND_ASSIGN_TOKENS.contains(tokenType); 931 } 932 933 /** 934 * Gets the name of the nearest parent ClassFrame. 935 * @return the name of the nearest parent ClassFrame. 936 */ 937 private String getNearestClassFrameName() { 938 AbstractFrame frame = current.peek(); 939 while (frame.getType() != FrameType.CLASS_FRAME) { 940 frame = frame.getParent(); 941 } 942 return frame.getFrameName(); 943 } 944 945 /** 946 * Checks if the token is a Lambda parameter. 947 * @param ast the {@code DetailAST} value of the token to be checked 948 * @return true if the token is a Lambda parameter 949 */ 950 private static boolean isLambdaParameter(DetailAST ast) { 951 DetailAST parent; 952 for (parent = ast.getParent(); parent != null; parent = parent.getParent()) { 953 if (parent.getType() == TokenTypes.LAMBDA) { 954 break; 955 } 956 } 957 final boolean isLambdaParameter; 958 if (parent == null) { 959 isLambdaParameter = false; 960 } 961 else if (ast.getType() == TokenTypes.PARAMETER_DEF) { 962 isLambdaParameter = true; 963 } 964 else { 965 final DetailAST lambdaParameters = parent.findFirstToken(TokenTypes.PARAMETERS); 966 if (lambdaParameters == null) { 967 isLambdaParameter = parent.getFirstChild().getText().equals(ast.getText()); 968 } 969 else { 970 isLambdaParameter = TokenUtil.findFirstTokenByPredicate(lambdaParameters, 971 paramDef -> { 972 final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT); 973 return param != null && param.getText().equals(ast.getText()); 974 }).isPresent(); 975 } 976 } 977 return isLambdaParameter; 978 } 979 980 /** An AbstractFrame type. */ 981 private enum FrameType { 982 983 /** Class frame type. */ 984 CLASS_FRAME, 985 /** Constructor frame type. */ 986 CTOR_FRAME, 987 /** Method frame type. */ 988 METHOD_FRAME, 989 /** Block frame type. */ 990 BLOCK_FRAME, 991 /** Catch frame type. */ 992 CATCH_FRAME, 993 /** Lambda frame type. */ 994 FOR_FRAME, 995 996 } 997 998 /** 999 * A declaration frame. 1000 */ 1001 private abstract static class AbstractFrame { 1002 1003 /** Set of name of variables declared in this frame. */ 1004 private final Set<DetailAST> varIdents; 1005 1006 /** Parent frame. */ 1007 private final AbstractFrame parent; 1008 1009 /** Name identifier token. */ 1010 private final DetailAST frameNameIdent; 1011 1012 /** 1013 * Constructor -- invocable only via super() from subclasses. 1014 * @param parent parent frame. 1015 * @param ident frame name ident. 1016 */ 1017 protected AbstractFrame(AbstractFrame parent, DetailAST ident) { 1018 this.parent = parent; 1019 frameNameIdent = ident; 1020 varIdents = new HashSet<>(); 1021 } 1022 1023 /** 1024 * Get the type of the frame. 1025 * @return a FrameType. 1026 */ 1027 protected abstract FrameType getType(); 1028 1029 /** 1030 * Add a name to the frame. 1031 * @param identToAdd the name we're adding. 1032 */ 1033 private void addIdent(DetailAST identToAdd) { 1034 varIdents.add(identToAdd); 1035 } 1036 1037 protected AbstractFrame getParent() { 1038 return parent; 1039 } 1040 1041 protected String getFrameName() { 1042 return frameNameIdent.getText(); 1043 } 1044 1045 public DetailAST getFrameNameIdent() { 1046 return frameNameIdent; 1047 } 1048 1049 /** 1050 * Check whether the frame contains a field or a variable with the given name. 1051 * @param nameToFind the IDENT ast of the name we're looking for. 1052 * @return whether it was found. 1053 */ 1054 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 1055 return containsFieldOrVariableDef(varIdents, nameToFind); 1056 } 1057 1058 /** 1059 * Check whether the frame contains a given name. 1060 * @param nameToFind IDENT ast of the name we're looking for. 1061 * @param lookForMethod whether we are looking for a method name. 1062 * @return whether it was found. 1063 */ 1064 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 1065 final AbstractFrame frame; 1066 1067 if (!lookForMethod 1068 && containsFieldOrVariable(nameToFind)) { 1069 frame = this; 1070 } 1071 else { 1072 frame = parent.getIfContains(nameToFind, lookForMethod); 1073 } 1074 return frame; 1075 } 1076 1077 /** 1078 * Whether the set contains a declaration with the text of the specified 1079 * IDENT ast and it is declared in a proper position. 1080 * @param set the set of declarations. 1081 * @param ident the specified IDENT ast. 1082 * @return true if the set contains a declaration with the text of the specified 1083 * IDENT ast and it is declared in a proper position. 1084 */ 1085 protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) { 1086 boolean result = false; 1087 for (DetailAST ast: set) { 1088 if (isProperDefinition(ident, ast)) { 1089 result = true; 1090 break; 1091 } 1092 } 1093 return result; 1094 } 1095 1096 /** 1097 * Whether the definition is correspondent to the IDENT. 1098 * @param ident the IDENT ast to check. 1099 * @param ast the IDENT ast of the definition to check. 1100 * @return true if ast is correspondent to ident. 1101 */ 1102 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1103 final String nameToFind = ident.getText(); 1104 return nameToFind.equals(ast.getText()) 1105 && checkPosition(ast, ident); 1106 } 1107 1108 /** 1109 * Whether the declaration is located before the checked ast. 1110 * @param ast1 the IDENT ast of the declaration. 1111 * @param ast2 the IDENT ast to check. 1112 * @return true, if the declaration is located before the checked ast. 1113 */ 1114 private static boolean checkPosition(DetailAST ast1, DetailAST ast2) { 1115 boolean result = false; 1116 if (ast1.getLineNo() < ast2.getLineNo() 1117 || ast1.getLineNo() == ast2.getLineNo() 1118 && ast1.getColumnNo() < ast2.getColumnNo()) { 1119 result = true; 1120 } 1121 return result; 1122 } 1123 1124 } 1125 1126 /** 1127 * A frame initiated at method definition; holds a method definition token. 1128 */ 1129 private static class MethodFrame extends AbstractFrame { 1130 1131 /** 1132 * Creates method frame. 1133 * @param parent parent frame. 1134 * @param ident method name identifier token. 1135 */ 1136 protected MethodFrame(AbstractFrame parent, DetailAST ident) { 1137 super(parent, ident); 1138 } 1139 1140 @Override 1141 protected FrameType getType() { 1142 return FrameType.METHOD_FRAME; 1143 } 1144 1145 } 1146 1147 /** 1148 * A frame initiated at constructor definition. 1149 */ 1150 private static class ConstructorFrame extends AbstractFrame { 1151 1152 /** 1153 * Creates a constructor frame. 1154 * @param parent parent frame. 1155 * @param ident frame name ident. 1156 */ 1157 protected ConstructorFrame(AbstractFrame parent, DetailAST ident) { 1158 super(parent, ident); 1159 } 1160 1161 @Override 1162 protected FrameType getType() { 1163 return FrameType.CTOR_FRAME; 1164 } 1165 1166 } 1167 1168 /** 1169 * A frame initiated at class, enum or interface definition; holds instance variable names. 1170 */ 1171 private static class ClassFrame extends AbstractFrame { 1172 1173 /** Set of idents of instance members declared in this frame. */ 1174 private final Set<DetailAST> instanceMembers; 1175 /** Set of idents of instance methods declared in this frame. */ 1176 private final Set<DetailAST> instanceMethods; 1177 /** Set of idents of variables declared in this frame. */ 1178 private final Set<DetailAST> staticMembers; 1179 /** Set of idents of static methods declared in this frame. */ 1180 private final Set<DetailAST> staticMethods; 1181 1182 /** 1183 * Creates new instance of ClassFrame. 1184 * @param parent parent frame. 1185 * @param ident frame name ident. 1186 */ 1187 ClassFrame(AbstractFrame parent, DetailAST ident) { 1188 super(parent, ident); 1189 instanceMembers = new HashSet<>(); 1190 instanceMethods = new HashSet<>(); 1191 staticMembers = new HashSet<>(); 1192 staticMethods = new HashSet<>(); 1193 } 1194 1195 @Override 1196 protected FrameType getType() { 1197 return FrameType.CLASS_FRAME; 1198 } 1199 1200 /** 1201 * Adds static member's ident. 1202 * @param ident an ident of static member of the class. 1203 */ 1204 public void addStaticMember(final DetailAST ident) { 1205 staticMembers.add(ident); 1206 } 1207 1208 /** 1209 * Adds static method's name. 1210 * @param ident an ident of static method of the class. 1211 */ 1212 public void addStaticMethod(final DetailAST ident) { 1213 staticMethods.add(ident); 1214 } 1215 1216 /** 1217 * Adds instance member's ident. 1218 * @param ident an ident of instance member of the class. 1219 */ 1220 public void addInstanceMember(final DetailAST ident) { 1221 instanceMembers.add(ident); 1222 } 1223 1224 /** 1225 * Adds instance method's name. 1226 * @param ident an ident of instance method of the class. 1227 */ 1228 public void addInstanceMethod(final DetailAST ident) { 1229 instanceMethods.add(ident); 1230 } 1231 1232 /** 1233 * Checks if a given name is a known instance member of the class. 1234 * @param ident the IDENT ast of the name to check. 1235 * @return true is the given name is a name of a known 1236 * instance member of the class. 1237 */ 1238 public boolean hasInstanceMember(final DetailAST ident) { 1239 return containsFieldOrVariableDef(instanceMembers, ident); 1240 } 1241 1242 /** 1243 * Checks if a given name is a known instance method of the class. 1244 * @param ident the IDENT ast of the method call to check. 1245 * @return true if the given ast is correspondent to a known 1246 * instance method of the class. 1247 */ 1248 public boolean hasInstanceMethod(final DetailAST ident) { 1249 return containsMethodDef(instanceMethods, ident); 1250 } 1251 1252 /** 1253 * Checks if a given name is a known static method of the class. 1254 * @param ident the IDENT ast of the method call to check. 1255 * @return true is the given ast is correspondent to a known 1256 * instance method of the class. 1257 */ 1258 public boolean hasStaticMethod(final DetailAST ident) { 1259 return containsMethodDef(staticMethods, ident); 1260 } 1261 1262 /** 1263 * Checks whether given instance member has final modifier. 1264 * @param instanceMember an instance member of a class. 1265 * @return true if given instance member has final modifier. 1266 */ 1267 public boolean hasFinalField(final DetailAST instanceMember) { 1268 boolean result = false; 1269 for (DetailAST member : instanceMembers) { 1270 final DetailAST mods = member.getParent().findFirstToken(TokenTypes.MODIFIERS); 1271 final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null; 1272 if (finalMod && member.equals(instanceMember)) { 1273 result = true; 1274 break; 1275 } 1276 } 1277 return result; 1278 } 1279 1280 @Override 1281 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 1282 return containsFieldOrVariableDef(instanceMembers, nameToFind) 1283 || containsFieldOrVariableDef(staticMembers, nameToFind); 1284 } 1285 1286 @Override 1287 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1288 final String nameToFind = ident.getText(); 1289 return nameToFind.equals(ast.getText()); 1290 } 1291 1292 @Override 1293 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 1294 AbstractFrame frame = null; 1295 1296 if (lookForMethod && containsMethod(nameToFind) 1297 || containsFieldOrVariable(nameToFind)) { 1298 frame = this; 1299 } 1300 else if (getParent() != null) { 1301 frame = getParent().getIfContains(nameToFind, lookForMethod); 1302 } 1303 return frame; 1304 } 1305 1306 /** 1307 * Check whether the frame contains a given method. 1308 * @param methodToFind the AST of the method to find. 1309 * @return true, if a method with the same name and number of parameters is found. 1310 */ 1311 private boolean containsMethod(DetailAST methodToFind) { 1312 return containsMethodDef(instanceMethods, methodToFind) 1313 || containsMethodDef(staticMethods, methodToFind); 1314 } 1315 1316 /** 1317 * Whether the set contains a method definition with the 1318 * same name and number of parameters. 1319 * @param set the set of definitions. 1320 * @param ident the specified method call IDENT ast. 1321 * @return true if the set contains a definition with the 1322 * same name and number of parameters. 1323 */ 1324 private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) { 1325 boolean result = false; 1326 for (DetailAST ast: set) { 1327 if (isSimilarSignature(ident, ast)) { 1328 result = true; 1329 break; 1330 } 1331 } 1332 return result; 1333 } 1334 1335 /** 1336 * Whether the method definition has the same name and number of parameters. 1337 * @param ident the specified method call IDENT ast. 1338 * @param ast the ast of a method definition to compare with. 1339 * @return true if a method definition has the same name and number of parameters 1340 * as the method call. 1341 */ 1342 private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) { 1343 boolean result = false; 1344 final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST); 1345 if (elistToken != null && ident.getText().equals(ast.getText())) { 1346 final int paramsNumber = 1347 ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount(); 1348 final int argsNumber = elistToken.getChildCount(); 1349 result = paramsNumber == argsNumber; 1350 } 1351 return result; 1352 } 1353 1354 } 1355 1356 /** 1357 * An anonymous class frame; holds instance variable names. 1358 */ 1359 private static class AnonymousClassFrame extends ClassFrame { 1360 1361 /** The name of the frame. */ 1362 private final String frameName; 1363 1364 /** 1365 * Creates anonymous class frame. 1366 * @param parent parent frame. 1367 * @param frameName name of the frame. 1368 */ 1369 protected AnonymousClassFrame(AbstractFrame parent, String frameName) { 1370 super(parent, null); 1371 this.frameName = frameName; 1372 } 1373 1374 @Override 1375 protected String getFrameName() { 1376 return frameName; 1377 } 1378 1379 } 1380 1381 /** 1382 * A frame initiated on entering a statement list; holds local variable names. 1383 */ 1384 private static class BlockFrame extends AbstractFrame { 1385 1386 /** 1387 * Creates block frame. 1388 * @param parent parent frame. 1389 * @param ident ident frame name ident. 1390 */ 1391 protected BlockFrame(AbstractFrame parent, DetailAST ident) { 1392 super(parent, ident); 1393 } 1394 1395 @Override 1396 protected FrameType getType() { 1397 return FrameType.BLOCK_FRAME; 1398 } 1399 1400 } 1401 1402 /** 1403 * A frame initiated on entering a catch block; holds local catch variable names. 1404 */ 1405 public static class CatchFrame extends AbstractFrame { 1406 1407 /** 1408 * Creates catch frame. 1409 * @param parent parent frame. 1410 * @param ident ident frame name ident. 1411 */ 1412 protected CatchFrame(AbstractFrame parent, DetailAST ident) { 1413 super(parent, ident); 1414 } 1415 1416 @Override 1417 public FrameType getType() { 1418 return FrameType.CATCH_FRAME; 1419 } 1420 1421 } 1422 1423 /** 1424 * A frame initiated on entering a for block; holds local for variable names. 1425 */ 1426 public static class ForFrame extends AbstractFrame { 1427 1428 /** 1429 * Creates for frame. 1430 * @param parent parent frame. 1431 * @param ident ident frame name ident. 1432 */ 1433 protected ForFrame(AbstractFrame parent, DetailAST ident) { 1434 super(parent, ident); 1435 } 1436 1437 @Override 1438 public FrameType getType() { 1439 return FrameType.FOR_FRAME; 1440 } 1441 1442 } 1443 1444}