001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.BitSet; 024import java.util.Deque; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.Map; 028import java.util.Queue; 029import java.util.Set; 030 031import javax.annotation.Nullable; 032 033import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 034import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 035import com.puppycrawl.tools.checkstyle.api.DetailAST; 036import com.puppycrawl.tools.checkstyle.api.TokenTypes; 037import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 038import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 039import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 040 041/** 042 * <div> 043 * Checks that references to instance variables and methods of the present 044 * object are explicitly of the form "this.varName" or "this.methodName(args)" 045 * and that those references don't rely on the default behavior when "this." is absent. 046 * </div> 047 * 048 * <p>Warning: the Check is very controversial if 'validateOnlyOverlapping' option is set to 'false' 049 * and not that actual nowadays.</p> 050 * 051 * <p>Rationale:</p> 052 * <ol> 053 * <li> 054 * The same notation/habit for C++ and Java (C++ have global methods, so having 055 * "this." do make sense in it to distinguish call of method of class 056 * instead of global). 057 * </li> 058 * <li> 059 * Non-IDE development (ease of refactoring, some clearness to distinguish 060 * static and non-static methods). 061 * </li> 062 * </ol> 063 * 064 * <p> 065 * Notes: 066 * Limitations: Nothing is currently done about static variables 067 * or catch-blocks. Static methods invoked on a class name seem to be OK; 068 * both the class name and the method name have a DOT parent. 069 * Non-static methods invoked on either this or a variable name seem to be 070 * OK, likewise. 071 * </p> 072 * 073 * @since 3.4 074 */ 075// -@cs[ClassDataAbstractionCoupling] This check requires to work with and identify many frames. 076@FileStatefulCheck 077public class RequireThisCheck extends AbstractCheck { 078 079 /** 080 * A key is pointing to the warning message text in "messages.properties" 081 * file. 082 */ 083 public static final String MSG_METHOD = "require.this.method"; 084 /** 085 * A key is pointing to the warning message text in "messages.properties" 086 * file. 087 */ 088 public static final String MSG_VARIABLE = "require.this.variable"; 089 090 /** Set of all declaration tokens. */ 091 private static final BitSet DECLARATION_TOKENS = TokenUtil.asBitSet( 092 TokenTypes.VARIABLE_DEF, 093 TokenTypes.CTOR_DEF, 094 TokenTypes.METHOD_DEF, 095 TokenTypes.CLASS_DEF, 096 TokenTypes.ENUM_DEF, 097 TokenTypes.ANNOTATION_DEF, 098 TokenTypes.INTERFACE_DEF, 099 TokenTypes.PARAMETER_DEF, 100 TokenTypes.TYPE_ARGUMENT, 101 TokenTypes.RECORD_DEF, 102 TokenTypes.RECORD_COMPONENT_DEF, 103 TokenTypes.RESOURCE 104 ); 105 /** Set of all assign tokens. */ 106 private static final BitSet ASSIGN_TOKENS = TokenUtil.asBitSet( 107 TokenTypes.ASSIGN, 108 TokenTypes.PLUS_ASSIGN, 109 TokenTypes.STAR_ASSIGN, 110 TokenTypes.DIV_ASSIGN, 111 TokenTypes.MOD_ASSIGN, 112 TokenTypes.SR_ASSIGN, 113 TokenTypes.BSR_ASSIGN, 114 TokenTypes.SL_ASSIGN, 115 TokenTypes.BAND_ASSIGN, 116 TokenTypes.BXOR_ASSIGN 117 ); 118 /** Set of all compound assign tokens. */ 119 private static final BitSet COMPOUND_ASSIGN_TOKENS = TokenUtil.asBitSet( 120 TokenTypes.PLUS_ASSIGN, 121 TokenTypes.STAR_ASSIGN, 122 TokenTypes.DIV_ASSIGN, 123 TokenTypes.MOD_ASSIGN, 124 TokenTypes.SR_ASSIGN, 125 TokenTypes.BSR_ASSIGN, 126 TokenTypes.SL_ASSIGN, 127 TokenTypes.BAND_ASSIGN, 128 TokenTypes.BXOR_ASSIGN 129 ); 130 131 /** Frame for the currently processed AST. */ 132 private final Deque<AbstractFrame> current = new ArrayDeque<>(); 133 134 /** Tree of all the parsed frames. */ 135 private Map<DetailAST, AbstractFrame> frames; 136 137 /** Control whether to check references to fields. */ 138 private boolean checkFields = true; 139 /** Control whether to check references to methods. */ 140 private boolean checkMethods = true; 141 /** Control whether to check only overlapping by variables or arguments. */ 142 private boolean validateOnlyOverlapping = true; 143 144 /** 145 * Creates a new {@code RequireThisCheck} instance. 146 */ 147 public RequireThisCheck() { 148 // no code by default 149 } 150 151 /** 152 * Setter to control whether to check references to fields. 153 * 154 * @param checkFields should we check fields usage or not 155 * @since 3.4 156 */ 157 public void setCheckFields(boolean checkFields) { 158 this.checkFields = checkFields; 159 } 160 161 /** 162 * Setter to control whether to check references to methods. 163 * 164 * @param checkMethods should we check methods usage or not 165 * @since 3.4 166 */ 167 public void setCheckMethods(boolean checkMethods) { 168 this.checkMethods = checkMethods; 169 } 170 171 /** 172 * Setter to control whether to check only overlapping by variables or arguments. 173 * 174 * @param validateOnlyOverlapping should we check only overlapping by variables or arguments 175 * @since 6.17 176 */ 177 public void setValidateOnlyOverlapping(boolean validateOnlyOverlapping) { 178 this.validateOnlyOverlapping = validateOnlyOverlapping; 179 } 180 181 @Override 182 public int[] getDefaultTokens() { 183 return getRequiredTokens(); 184 } 185 186 @Override 187 public int[] getRequiredTokens() { 188 return new int[] { 189 TokenTypes.CLASS_DEF, 190 TokenTypes.INTERFACE_DEF, 191 TokenTypes.ENUM_DEF, 192 TokenTypes.ANNOTATION_DEF, 193 TokenTypes.CTOR_DEF, 194 TokenTypes.METHOD_DEF, 195 TokenTypes.LITERAL_FOR, 196 TokenTypes.SLIST, 197 TokenTypes.IDENT, 198 TokenTypes.RECORD_DEF, 199 TokenTypes.COMPACT_CTOR_DEF, 200 TokenTypes.LITERAL_TRY, 201 TokenTypes.RESOURCE, 202 TokenTypes.COMPACT_COMPILATION_UNIT, 203 }; 204 } 205 206 @Override 207 public int[] getAcceptableTokens() { 208 return getRequiredTokens(); 209 } 210 211 @Override 212 public void beginTree(DetailAST rootAST) { 213 frames = new HashMap<>(); 214 current.clear(); 215 216 final Deque<AbstractFrame> frameStack = new ArrayDeque<>(); 217 DetailAST curNode = rootAST; 218 while (curNode != null) { 219 collectDeclarations(frameStack, curNode); 220 DetailAST toVisit = curNode.getFirstChild(); 221 while (curNode != null && toVisit == null) { 222 endCollectingDeclarations(frameStack, curNode); 223 toVisit = curNode.getNextSibling(); 224 curNode = curNode.getParent(); 225 } 226 curNode = toVisit; 227 } 228 } 229 230 @Override 231 public void visitToken(DetailAST ast) { 232 switch (ast.getType()) { 233 case TokenTypes.IDENT -> processIdent(ast); 234 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF, 235 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF, 236 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_FOR, TokenTypes.RECORD_DEF, 237 TokenTypes.COMPACT_COMPILATION_UNIT -> 238 current.push(frames.get(ast)); 239 case TokenTypes.LITERAL_TRY -> { 240 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) { 241 current.push(frames.get(ast)); 242 } 243 } 244 default -> { 245 // Do nothing 246 } 247 } 248 } 249 250 @Override 251 public void leaveToken(DetailAST ast) { 252 switch (ast.getType()) { 253 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF, 254 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF, 255 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_FOR, 256 TokenTypes.RECORD_DEF -> current.pop(); 257 case TokenTypes.LITERAL_TRY -> { 258 if (current.peek().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) { 259 current.pop(); 260 } 261 } 262 default -> { 263 // Do nothing 264 } 265 } 266 } 267 268 /** 269 * Checks if a given IDENT is method call or field name which 270 * requires explicit {@code this} qualifier. 271 * 272 * @param ast IDENT to check. 273 */ 274 private void processIdent(DetailAST ast) { 275 if (!shouldSkipAnnotationContext(ast)) { 276 final int parentType = ast.getParent().getType(); 277 if (parentType == TokenTypes.METHOD_CALL) { 278 if (checkMethods) { 279 final AbstractFrame frame = getMethodWithoutThis(ast); 280 if (frame != null) { 281 logViolation(MSG_METHOD, ast, frame); 282 } 283 } 284 } 285 else { 286 if (checkFields) { 287 final AbstractFrame frame = getFieldWithoutThis(ast, parentType); 288 final boolean canUseThis = !isInCompactConstructor(ast); 289 if (frame != null && canUseThis) { 290 logViolation(MSG_VARIABLE, ast, frame); 291 } 292 } 293 } 294 } 295 } 296 297 /** 298 * Determines whether the given IDENT should be skipped because it is 299 * in an annotation context. When {@code validateOnlyOverlapping} is 300 * {@code true}, all annotation contexts are skipped. When it is 301 * {@code false}, only annotation structural elements (member names, 302 * type names, and annotation field defaults) are skipped, while actual 303 * field reference values inside annotations are still processed. 304 * 305 * @param ast IDENT token 306 * @return true if the IDENT should be skipped 307 */ 308 private static boolean shouldSkipAnnotationContext(DetailAST ast) { 309 return isInsideAnnotationFieldDef(ast) || isAnnotationStructuralElement(ast); 310 } 311 312 /** 313 * Checks whether the given IDENT is inside an annotation field definition 314 * (e.g., default values in {@code @interface} members). 315 * 316 * @param ast IDENT token 317 * @return true if IDENT is inside an annotation field definition 318 */ 319 private static boolean isInsideAnnotationFieldDef(DetailAST ast) { 320 DetailAST current = ast; 321 boolean insideAnnotationFieldDef = false; 322 while (current != null) { 323 if (current.getType() == TokenTypes.ANNOTATION_FIELD_DEF) { 324 insideAnnotationFieldDef = true; 325 break; 326 } 327 current = current.getParent(); 328 } 329 return insideAnnotationFieldDef; 330 } 331 332 /** 333 * Checks if an IDENT is an annotation structural element — either 334 * an annotation type name (e.g., {@code SuppressWarnings} in 335 * {@code @SuppressWarnings}) or an annotation member name (e.g., 336 * {@code value} in {@code value = "unused"}). 337 * 338 * @param ast IDENT token 339 * @return true if the IDENT is an annotation type name or member name 340 */ 341 private static boolean isAnnotationStructuralElement(DetailAST ast) { 342 DetailAST current = ast.getParent(); 343 final int parentType = current.getType(); 344 while (current.getType() == TokenTypes.DOT) { 345 current = current.getParent(); 346 } 347 final int topType = current.getType(); 348 return topType == TokenTypes.ANNOTATION 349 || parentType == TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR; 350 } 351 352 /** 353 * Helper method to log a Violation. 354 * 355 * @param msgKey key to locale message format. 356 * @param ast a node to get line id column numbers associated with the message. 357 * @param frame the class frame where the violation is found. 358 */ 359 private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) { 360 if (frame.getFrameName().equals(getNearestClassFrameName())) { 361 log(ast, msgKey, ast.getText(), ""); 362 } 363 else if (!(frame instanceof AnonymousClassFrame) 364 && !(frame instanceof CompactCompilationUnitFrame)) { 365 log(ast, msgKey, ast.getText(), frame.getFrameName() + '.'); 366 } 367 } 368 369 /** 370 * Returns the frame where the field is declared, if the given field is used without 371 * 'this', and null otherwise. 372 * 373 * @param ast field definition ast token. 374 * @param parentType type of the parent. 375 * @return the frame where the field is declared, if the given field is used without 376 * 'this' and null otherwise. 377 */ 378 private AbstractFrame getFieldWithoutThis(DetailAST ast, int parentType) { 379 final boolean importOrPackage = ScopeUtil.getSurroundingScope(ast).isEmpty(); 380 final boolean typeName = parentType == TokenTypes.TYPE 381 || parentType == TokenTypes.LITERAL_NEW; 382 AbstractFrame frame = null; 383 384 if (!importOrPackage 385 && !typeName 386 && !DECLARATION_TOKENS.get(parentType) 387 && !isLambdaParameter(ast)) { 388 final AbstractFrame fieldFrame = findClassFrame(ast, LookMode.NO_LOOK_FOR_METHOD); 389 390 if (fieldFrame != null && ((ClassFrame) fieldFrame).hasInstanceMember(ast)) { 391 frame = getClassFrameWhereViolationIsFound(ast); 392 } 393 } 394 return frame; 395 } 396 397 /** 398 * Return whether ast is in a COMPACT_CTOR_DEF. 399 * 400 * @param ast The token to check 401 * @return true if ast is in a COMPACT_CTOR_DEF, false otherwise 402 */ 403 private static boolean isInCompactConstructor(DetailAST ast) { 404 boolean isInCompactCtor = false; 405 DetailAST parent = ast; 406 while (parent != null) { 407 if (parent.getType() == TokenTypes.COMPACT_CTOR_DEF) { 408 isInCompactCtor = true; 409 break; 410 } 411 parent = parent.getParent(); 412 } 413 return isInCompactCtor; 414 } 415 416 /** 417 * Parses the next AST for declarations. 418 * 419 * @param frameStack stack containing the FrameTree being built. 420 * @param ast AST to parse. 421 */ 422 // -@cs[JavaNCSS] This method is a big switch and is too hard to remove. 423 private static void collectDeclarations(Deque<AbstractFrame> frameStack, DetailAST ast) { 424 final AbstractFrame frame = frameStack.peek(); 425 426 switch (ast.getType()) { 427 case TokenTypes.VARIABLE_DEF -> collectVariableDeclarations(ast, frame); 428 429 case TokenTypes.RECORD_COMPONENT_DEF -> { 430 final DetailAST componentIdent = ast.findFirstToken(TokenTypes.IDENT); 431 ((ClassFrame) frame).addInstanceMember(componentIdent); 432 } 433 434 case TokenTypes.PARAMETER_DEF -> { 435 if (!CheckUtil.isReceiverParameter(ast) && !isLambdaParameter(ast)) { 436 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT); 437 frame.addIdent(parameterIdent); 438 } 439 } 440 441 case TokenTypes.RESOURCE -> { 442 final DetailAST resourceIdent = ast.findFirstToken(TokenTypes.IDENT); 443 if (resourceIdent != null) { 444 frame.addIdent(resourceIdent); 445 } 446 } 447 448 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF, 449 TokenTypes.ANNOTATION_DEF, TokenTypes.RECORD_DEF -> { 450 final DetailAST classFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 451 frameStack.addFirst(new ClassFrame(frame, classFrameNameIdent)); 452 } 453 454 case TokenTypes.COMPACT_COMPILATION_UNIT -> 455 frameStack.addFirst(new CompactCompilationUnitFrame(frame, ast)); 456 457 case TokenTypes.SLIST -> frameStack.addFirst(new BlockFrame(frame, ast)); 458 459 case TokenTypes.METHOD_DEF -> collectMethodDeclarations(frameStack, ast, frame); 460 461 case TokenTypes.CTOR_DEF, TokenTypes.COMPACT_CTOR_DEF -> { 462 final DetailAST ctorFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 463 frameStack.addFirst(new ConstructorFrame(frame, ctorFrameNameIdent)); 464 } 465 466 case TokenTypes.ENUM_CONSTANT_DEF -> { 467 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 468 ((ClassFrame) frame).addStaticMember(ident); 469 } 470 471 case TokenTypes.LITERAL_CATCH -> { 472 final AbstractFrame catchFrame = new CatchFrame(frame, ast); 473 frameStack.addFirst(catchFrame); 474 } 475 476 case TokenTypes.LITERAL_FOR -> { 477 final AbstractFrame forFrame = new ForFrame(frame, ast); 478 frameStack.addFirst(forFrame); 479 } 480 481 case TokenTypes.LITERAL_NEW -> { 482 final DetailAST lastChild = ast.getLastChild(); 483 if (lastChild != null && lastChild.getType() == TokenTypes.OBJBLOCK) { 484 frameStack.addFirst(new AnonymousClassFrame(frame, ast.toString())); 485 } 486 } 487 488 case TokenTypes.LITERAL_TRY -> { 489 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) { 490 frameStack.addFirst(new TryWithResourcesFrame(frame, ast)); 491 } 492 } 493 494 default -> { 495 // do nothing 496 } 497 } 498 } 499 500 /** 501 * Collects variable declarations. 502 * 503 * @param ast variable token. 504 * @param frame current frame. 505 */ 506 private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) { 507 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 508 if (frame.getType() == FrameType.CLASS_FRAME) { 509 final DetailAST mods = 510 ast.findFirstToken(TokenTypes.MODIFIERS); 511 if (ScopeUtil.isInInterfaceBlock(ast) 512 || ScopeUtil.isInAnnotationBlock(ast) 513 || mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null) { 514 ((ClassFrame) frame).addStaticMember(ident); 515 } 516 else { 517 ((ClassFrame) frame).addInstanceMember(ident); 518 } 519 } 520 else { 521 frame.addIdent(ident); 522 } 523 } 524 525 /** 526 * Collects {@code METHOD_DEF} declarations. 527 * 528 * @param frameStack stack containing the FrameTree being built. 529 * @param ast AST to parse. 530 * @param frame current frame. 531 */ 532 private static void collectMethodDeclarations(Deque<AbstractFrame> frameStack, 533 DetailAST ast, AbstractFrame frame) { 534 final DetailAST methodFrameNameIdent = ast.findFirstToken(TokenTypes.IDENT); 535 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS); 536 if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 537 ((ClassFrame) frame).addInstanceMethod(methodFrameNameIdent); 538 } 539 else { 540 ((ClassFrame) frame).addStaticMethod(methodFrameNameIdent); 541 } 542 frameStack.addFirst(new MethodFrame(frame, methodFrameNameIdent)); 543 } 544 545 /** 546 * Ends parsing of the AST for declarations. 547 * 548 * @param frameStack Stack containing the FrameTree being built. 549 * @param ast AST that was parsed. 550 */ 551 private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, DetailAST ast) { 552 switch (ast.getType()) { 553 case TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF, TokenTypes.ENUM_DEF, 554 TokenTypes.ANNOTATION_DEF, TokenTypes.SLIST, TokenTypes.METHOD_DEF, 555 TokenTypes.CTOR_DEF, TokenTypes.LITERAL_CATCH, TokenTypes.LITERAL_FOR, 556 TokenTypes.RECORD_DEF, TokenTypes.COMPACT_CTOR_DEF, 557 TokenTypes.COMPACT_COMPILATION_UNIT -> 558 frames.put(ast, frameStack.poll()); 559 560 case TokenTypes.LITERAL_NEW -> { 561 final DetailAST lastChild = ast.getLastChild(); 562 if (lastChild != null && lastChild.getType() == TokenTypes.OBJBLOCK) { 563 frameStack.remove(); 564 } 565 } 566 567 case TokenTypes.LITERAL_TRY -> { 568 if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) { 569 frames.put(ast, frameStack.poll()); 570 } 571 } 572 573 default -> { 574 // do nothing 575 } 576 } 577 } 578 579 /** 580 * Returns the class frame where violation is found (where the field is used without 'this') 581 * or null otherwise. 582 * 583 * @param ast IDENT ast to check. 584 * @return the class frame where violation is found or null otherwise. 585 */ 586 @Nullable 587 private AbstractFrame getClassFrameWhereViolationIsFound(DetailAST ast) { 588 AbstractFrame frameWhereViolationIsFound = null; 589 final AbstractFrame variableDeclarationFrame = findFrame(ast, LookMode.NO_LOOK_FOR_METHOD); 590 final FrameType variableDeclarationFrameType = variableDeclarationFrame.getType(); 591 592 if (variableDeclarationFrameType == FrameType.CLASS_FRAME 593 && isViolationNoOverlapping(ast)) { 594 frameWhereViolationIsFound = variableDeclarationFrame; 595 } 596 else if (variableDeclarationFrameType == FrameType.METHOD_FRAME) { 597 frameWhereViolationIsFound = getFrameForMethod(ast, variableDeclarationFrame); 598 } 599 else if (variableDeclarationFrameType == FrameType.CTOR_FRAME 600 && isOverlappingByArgument(ast) 601 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast)) { 602 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD); 603 } 604 else if (variableDeclarationFrameType == FrameType.BLOCK_FRAME 605 && isViolationForBlockFrame(ast, variableDeclarationFrame)) { 606 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD); 607 } 608 return frameWhereViolationIsFound; 609 } 610 611 /** 612 * Checks if a violation occurred for a CLASS_FRAME when not overlapping. 613 * 614 * @param ast IDENT ast to check. 615 * @return true if a violation occurred. 616 */ 617 private boolean isViolationNoOverlapping(DetailAST ast) { 618 final DetailAST prevSibling = ast.getPreviousSibling(); 619 final int parentType = ast.getParent().getType(); 620 return !validateOnlyOverlapping 621 && (prevSibling == null 622 || parentType != TokenTypes.DOT && parentType != TokenTypes.METHOD_REF) 623 && canBeReferencedFromStaticContext(ast); 624 } 625 626 /** 627 * Checks if a violation occurred for a BLOCK_FRAME. 628 * 629 * @param ast IDENT ast to check. 630 * @param variableDeclarationFrame the frame where the variable is declared. 631 * @return true if a violation occurred. 632 */ 633 private boolean isViolationForBlockFrame(DetailAST ast, 634 AbstractFrame variableDeclarationFrame) { 635 return isOverlappingByLocalVariable(ast) 636 && canAssignValueToClassField(ast) 637 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 638 && !isReturnedVariable(variableDeclarationFrame, ast) 639 && canBeReferencedFromStaticContext(ast); 640 } 641 642 /** 643 * Returns the class frame where violation is found (where the field is used without 'this') 644 * or null otherwise. 645 * 646 * @param ast IDENT ast to check. 647 * @param variableDeclarationFrame the frame where the variable is declared. 648 * @return the class frame where violation is found or null otherwise. 649 */ 650 private AbstractFrame getFrameForMethod(DetailAST ast, 651 AbstractFrame variableDeclarationFrame) { 652 AbstractFrame frameWhereViolationIsFound = null; 653 if (isOverlappingByArgument(ast)) { 654 if (isViolationForMethodOverlapping(ast, variableDeclarationFrame)) { 655 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD); 656 } 657 } 658 else if (isViolationForMethodNoOverlapping(ast, variableDeclarationFrame)) { 659 frameWhereViolationIsFound = findFrame(ast, LookMode.LOOK_FOR_METHOD); 660 } 661 return frameWhereViolationIsFound; 662 } 663 664 /** 665 * Checks if a violation occurred for a METHOD_FRAME when overlapping. 666 * 667 * @param ast IDENT ast to check. 668 * @param variableDeclarationFrame the frame where the variable is declared. 669 * @return true if a violation occurred. 670 */ 671 private boolean isViolationForMethodOverlapping(DetailAST ast, 672 AbstractFrame variableDeclarationFrame) { 673 return !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 674 && !isReturnedVariable(variableDeclarationFrame, ast) 675 && canBeReferencedFromStaticContext(ast) 676 && canAssignValueToClassField(ast); 677 } 678 679 /** 680 * Checks if a violation occurred for a METHOD_FRAME when not overlapping. 681 * 682 * @param ast IDENT ast to check. 683 * @param variableDeclarationFrame the frame where the variable is declared. 684 * @return true if a violation occurred. 685 */ 686 private boolean isViolationForMethodNoOverlapping(DetailAST ast, 687 AbstractFrame variableDeclarationFrame) { 688 final DetailAST prevSibling = ast.getPreviousSibling(); 689 return !validateOnlyOverlapping 690 && prevSibling == null 691 && ASSIGN_TOKENS.get(ast.getParent().getType()) 692 && !isUserDefinedArrangementOfThis(variableDeclarationFrame, ast) 693 && canBeReferencedFromStaticContext(ast) 694 && canAssignValueToClassField(ast); 695 } 696 697 /** 698 * Checks whether user arranges 'this' for variable in method, constructor, or block on his own. 699 * 700 * @param currentFrame current frame. 701 * @param ident ident token. 702 * @return true if user arranges 'this' for variable in method, constructor, 703 * or block on his own. 704 */ 705 private static boolean isUserDefinedArrangementOfThis(AbstractFrame currentFrame, 706 DetailAST ident) { 707 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 708 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 709 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 710 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 711 712 boolean userDefinedArrangementOfThis = false; 713 714 final Set<DetailAST> variableUsagesInsideBlock = 715 getAllTokensWhichAreEqualToCurrent(definitionToken, ident, 716 blockEndToken.getLineNo()); 717 718 for (DetailAST variableUsage : variableUsagesInsideBlock) { 719 final DetailAST prevSibling = variableUsage.getPreviousSibling(); 720 if (prevSibling != null 721 && prevSibling.getType() == TokenTypes.LITERAL_THIS) { 722 userDefinedArrangementOfThis = true; 723 break; 724 } 725 } 726 return userDefinedArrangementOfThis; 727 } 728 729 /** 730 * Returns the token which ends the code block. 731 * 732 * @param blockNameIdent block name identifier. 733 * @param blockStartToken token which starts the block. 734 * @return the token which ends the code block. 735 */ 736 private static DetailAST getBlockEndToken(DetailAST blockNameIdent, DetailAST blockStartToken) { 737 DetailAST blockEndToken = null; 738 final DetailAST blockNameIdentParent = blockNameIdent.getParent(); 739 if (blockNameIdentParent.getType() == TokenTypes.CASE_GROUP) { 740 blockEndToken = blockNameIdentParent.getNextSibling(); 741 } 742 else { 743 final Set<DetailAST> rcurlyTokens = getAllTokensOfType(blockNameIdent, 744 TokenTypes.RCURLY); 745 for (DetailAST currentRcurly : rcurlyTokens) { 746 final DetailAST parent = currentRcurly.getParent(); 747 if (TokenUtil.areOnSameLine(blockStartToken, parent)) { 748 blockEndToken = currentRcurly; 749 } 750 } 751 } 752 return blockEndToken; 753 } 754 755 /** 756 * Checks whether the current variable is returned from the method. 757 * 758 * @param currentFrame current frame. 759 * @param ident variable ident token. 760 * @return true if the current variable is returned from the method. 761 */ 762 private static boolean isReturnedVariable(AbstractFrame currentFrame, DetailAST ident) { 763 final DetailAST blockFrameNameIdent = currentFrame.getFrameNameIdent(); 764 final DetailAST definitionToken = blockFrameNameIdent.getParent(); 765 final DetailAST blockStartToken = definitionToken.findFirstToken(TokenTypes.SLIST); 766 final DetailAST blockEndToken = getBlockEndToken(blockFrameNameIdent, blockStartToken); 767 768 final Set<DetailAST> returnsInsideBlock = getAllTokensOfType(definitionToken, 769 TokenTypes.LITERAL_RETURN, blockEndToken.getLineNo()); 770 771 return returnsInsideBlock.stream() 772 .anyMatch(returnToken -> isAstInside(returnToken, ident)); 773 } 774 775 /** 776 * Checks if the given {@code ast} is equal to the {@code tree} or a child of it. 777 * 778 * @param tree The tree to search. 779 * @param ast The AST to look for. 780 * @return {@code true} if the {@code ast} was found. 781 */ 782 private static boolean isAstInside(DetailAST tree, DetailAST ast) { 783 boolean result = false; 784 785 if (isAstSimilar(tree, ast)) { 786 result = true; 787 } 788 else { 789 for (DetailAST child = tree.getFirstChild(); child != null 790 && !result; child = child.getNextSibling()) { 791 result = isAstInside(child, ast); 792 } 793 } 794 795 return result; 796 } 797 798 /** 799 * Checks whether a field can be referenced from a static context. 800 * 801 * @param ident ident token. 802 * @return true if field can be referenced from a static context. 803 */ 804 private static boolean canBeReferencedFromStaticContext(DetailAST ident) { 805 boolean staticContext = false; 806 807 final DetailAST codeBlockDefinition = getCodeBlockDefinitionToken(ident); 808 if (codeBlockDefinition != null) { 809 final DetailAST modifiers = codeBlockDefinition.getFirstChild(); 810 staticContext = codeBlockDefinition.getType() == TokenTypes.STATIC_INIT 811 || modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null; 812 } 813 return !staticContext; 814 } 815 816 /** 817 * Returns code block definition token for current identifier. 818 * 819 * @param ident ident token. 820 * @return code block definition token for current identifier or null if code block 821 * definition was not found. 822 */ 823 private static DetailAST getCodeBlockDefinitionToken(DetailAST ident) { 824 DetailAST parent = ident; 825 while (parent != null 826 && parent.getType() != TokenTypes.METHOD_DEF 827 && parent.getType() != TokenTypes.STATIC_INIT) { 828 parent = parent.getParent(); 829 } 830 return parent; 831 } 832 833 /** 834 * Checks whether a value can be assigned to a field. 835 * A value can be assigned to a final field only in constructor block. If there is a method 836 * block, value assignment can be performed only to non final field. 837 * 838 * @param ast an identifier token. 839 * @return true if a value can be assigned to a field. 840 */ 841 private boolean canAssignValueToClassField(DetailAST ast) { 842 AbstractFrame fieldUsageFrame = findFrame(ast, LookMode.NO_LOOK_FOR_METHOD); 843 while (fieldUsageFrame.getType() == FrameType.BLOCK_FRAME) { 844 fieldUsageFrame = fieldUsageFrame.getParent(); 845 } 846 final boolean fieldUsageInConstructor = 847 fieldUsageFrame.getType() == FrameType.CTOR_FRAME; 848 849 final AbstractFrame declarationFrame = findFrame(ast, LookMode.LOOK_FOR_METHOD); 850 final boolean finalField = ((ClassFrame) declarationFrame).hasFinalField(ast); 851 852 return fieldUsageInConstructor || !finalField; 853 } 854 855 /** 856 * Checks whether an overlapping by method or constructor argument takes place. 857 * 858 * @param ast an identifier. 859 * @return true if an overlapping by method or constructor argument takes place. 860 */ 861 private boolean isOverlappingByArgument(DetailAST ast) { 862 boolean overlapping = false; 863 final DetailAST parent = ast.getParent(); 864 final DetailAST sibling = ast.getNextSibling(); 865 if (sibling != null && ASSIGN_TOKENS.get(parent.getType())) { 866 if (COMPOUND_ASSIGN_TOKENS.get(parent.getType())) { 867 overlapping = true; 868 } 869 else { 870 final ClassFrame classFrame = (ClassFrame) findFrame(ast, LookMode.LOOK_FOR_METHOD); 871 final Set<DetailAST> exprIdents = getAllTokensOfType(sibling, TokenTypes.IDENT); 872 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 873 } 874 } 875 return overlapping; 876 } 877 878 /** 879 * Checks whether an overlapping by local variable takes place. 880 * 881 * @param ast an identifier. 882 * @return true if an overlapping by local variable takes place. 883 */ 884 private boolean isOverlappingByLocalVariable(DetailAST ast) { 885 boolean overlapping = false; 886 final DetailAST parent = ast.getParent(); 887 if (ASSIGN_TOKENS.get(parent.getType())) { 888 final ClassFrame classFrame = (ClassFrame) findFrame(ast, LookMode.LOOK_FOR_METHOD); 889 final Set<DetailAST> exprIdents = 890 getAllTokensOfType(ast.getNextSibling(), TokenTypes.IDENT); 891 overlapping = classFrame.containsFieldOrVariableDef(exprIdents, ast); 892 } 893 return overlapping; 894 } 895 896 /** 897 * Collects all tokens of specific type starting with the current ast node. 898 * 899 * @param ast ast node. 900 * @param tokenType token type. 901 * @return a set of all tokens of specific type starting with the current ast node. 902 */ 903 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 904 DetailAST vertex = ast; 905 final Set<DetailAST> result = new HashSet<>(); 906 final Deque<DetailAST> stack = new ArrayDeque<>(); 907 while (vertex != null || !stack.isEmpty()) { 908 if (!stack.isEmpty()) { 909 vertex = stack.pop(); 910 } 911 while (vertex != null) { 912 if (vertex.getType() == tokenType) { 913 result.add(vertex); 914 } 915 if (vertex.getNextSibling() != null) { 916 stack.push(vertex.getNextSibling()); 917 } 918 vertex = vertex.getFirstChild(); 919 } 920 } 921 return result; 922 } 923 924 /** 925 * Collects all tokens of specific type starting with the current ast node and which line 926 * number is lower or equal to the end line number. 927 * 928 * @param ast ast node. 929 * @param tokenType token type. 930 * @param endLineNumber end line number. 931 * @return a set of all tokens of specific type starting with the current ast node and which 932 * line number is lower or equal to the end line number. 933 */ 934 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType, 935 int endLineNumber) { 936 DetailAST vertex = ast; 937 final Set<DetailAST> result = new HashSet<>(); 938 final Deque<DetailAST> stack = new ArrayDeque<>(); 939 while (vertex != null || !stack.isEmpty()) { 940 if (!stack.isEmpty()) { 941 vertex = stack.pop(); 942 } 943 while (vertex != null) { 944 if (tokenType == vertex.getType() 945 && vertex.getLineNo() <= endLineNumber) { 946 result.add(vertex); 947 } 948 if (vertex.getNextSibling() != null) { 949 stack.push(vertex.getNextSibling()); 950 } 951 vertex = vertex.getFirstChild(); 952 } 953 } 954 return result; 955 } 956 957 /** 958 * Collects all tokens which are equal to current token starting with the current ast node and 959 * which line number is lower or equal to the end line number. 960 * 961 * @param ast ast node. 962 * @param token token. 963 * @param endLineNumber end line number. 964 * @return a set of tokens which are equal to current token starting with the current ast node 965 * and which line number is lower or equal to the end line number. 966 */ 967 private static Set<DetailAST> getAllTokensWhichAreEqualToCurrent(DetailAST ast, DetailAST token, 968 int endLineNumber) { 969 DetailAST vertex = ast; 970 final Set<DetailAST> result = new HashSet<>(); 971 final Deque<DetailAST> stack = new ArrayDeque<>(); 972 while (vertex != null || !stack.isEmpty()) { 973 if (!stack.isEmpty()) { 974 vertex = stack.pop(); 975 } 976 while (vertex != null) { 977 if (isAstSimilar(token, vertex) 978 && vertex.getLineNo() <= endLineNumber) { 979 result.add(vertex); 980 } 981 if (vertex.getNextSibling() != null) { 982 stack.push(vertex.getNextSibling()); 983 } 984 vertex = vertex.getFirstChild(); 985 } 986 } 987 return result; 988 } 989 990 /** 991 * Returns the frame where the method is declared, if the given method is used without 992 * 'this' and null otherwise. 993 * 994 * @param ast the IDENT ast of the name to check. 995 * @return the frame where the method is declared, if the given method is used without 996 * 'this' and null otherwise. 997 */ 998 private AbstractFrame getMethodWithoutThis(DetailAST ast) { 999 AbstractFrame result = null; 1000 if (!validateOnlyOverlapping) { 1001 final AbstractFrame frame = findFrame(ast, LookMode.LOOK_FOR_METHOD); 1002 if (frame != null 1003 && ((ClassFrame) frame).hasInstanceMethod(ast) 1004 && !((ClassFrame) frame).hasStaticMethod(ast)) { 1005 result = frame; 1006 } 1007 } 1008 return result; 1009 } 1010 1011 /** 1012 * Find the class frame containing declaration. 1013 * 1014 * @param name IDENT ast of the declaration to find. 1015 * @param lookMode mode defining whether we are looking for a method name. 1016 * @return AbstractFrame containing declaration or null. 1017 */ 1018 private AbstractFrame findClassFrame(DetailAST name, LookMode lookMode) { 1019 AbstractFrame frame = current.peek(); 1020 1021 while (true) { 1022 frame = findFrame(frame, name, lookMode); 1023 1024 if (frame == null || frame instanceof ClassFrame) { 1025 break; 1026 } 1027 1028 frame = frame.getParent(); 1029 } 1030 1031 return frame; 1032 } 1033 1034 /** 1035 * Find frame containing declaration. 1036 * 1037 * @param name IDENT ast of the declaration to find. 1038 * @param lookMode mode defining whether we are looking for a method name. 1039 * @return AbstractFrame containing declaration or null. 1040 */ 1041 private AbstractFrame findFrame(DetailAST name, LookMode lookMode) { 1042 return findFrame(current.peek(), name, lookMode); 1043 } 1044 1045 /** 1046 * Find frame containing declaration. 1047 * 1048 * @param frame The parent frame to searching in. 1049 * @param name IDENT ast of the declaration to find. 1050 * @param lookMode mode defining whether we are looking for a method name. 1051 * @return AbstractFrame containing declaration or null. 1052 */ 1053 private static AbstractFrame findFrame(AbstractFrame frame, DetailAST name, 1054 LookMode lookMode) { 1055 return frame.getIfContains(name, lookMode); 1056 } 1057 1058 /** 1059 * Gets the name of the nearest parent ClassFrame. 1060 * 1061 * @return the name of the nearest parent ClassFrame. 1062 */ 1063 private String getNearestClassFrameName() { 1064 AbstractFrame frame = current.peek(); 1065 while (frame.getType() != FrameType.CLASS_FRAME) { 1066 frame = frame.getParent(); 1067 } 1068 return frame.getFrameName(); 1069 } 1070 1071 /** 1072 * Checks if the token is a Lambda parameter. 1073 * 1074 * @param ast the {@code DetailAST} value of the token to be checked 1075 * @return true if the token is a Lambda parameter 1076 */ 1077 private static boolean isLambdaParameter(DetailAST ast) { 1078 boolean result = false; 1079 for (DetailAST parent = ast; parent != null; parent = parent.getParent()) { 1080 if (parent.getType() == TokenTypes.LAMBDA) { 1081 result = !isInsideTypeDefInsideLambda(ast) 1082 && isMatchingLambdaParam(ast, parent); 1083 break; 1084 } 1085 } 1086 return result; 1087 } 1088 1089 /** 1090 * Checks whether the given AST matches a parameter of the specified lambda. 1091 * 1092 * @param ast the AST node to check. 1093 * @param lambda the lambda expression AST node. 1094 * @return true if the AST matches a lambda parameter. 1095 */ 1096 private static boolean isMatchingLambdaParam(DetailAST ast, DetailAST lambda) { 1097 final boolean isMatchingParam; 1098 if (ast.getType() == TokenTypes.PARAMETER_DEF) { 1099 isMatchingParam = true; 1100 } 1101 else { 1102 final DetailAST lambdaParameters = lambda.findFirstToken(TokenTypes.PARAMETERS); 1103 if (lambdaParameters == null) { 1104 isMatchingParam = lambda.getFirstChild().getText().equals(ast.getText()); 1105 } 1106 else { 1107 isMatchingParam = TokenUtil.findFirstTokenByPredicate(lambdaParameters, 1108 paramDef -> { 1109 final DetailAST param = paramDef.findFirstToken(TokenTypes.IDENT); 1110 return param != null && param.getText().equals(ast.getText()); 1111 }).isPresent(); 1112 } 1113 } 1114 return isMatchingParam; 1115 } 1116 1117 /** 1118 * Checks if the given AST is inside a type declaration (class, interface, enum, 1119 * record, or anonymous class) that is nested within the enclosing lambda. 1120 * 1121 * @param ast the AST node to check. 1122 * @return true if there is a type declaration boundary between {@code ast} and the lambda. 1123 */ 1124 private static boolean isInsideTypeDefInsideLambda(DetailAST ast) { 1125 boolean isInside = false; 1126 for (DetailAST parent = ast; parent.getType() != TokenTypes.LAMBDA; 1127 parent = parent.getParent()) { 1128 if (parent.getType() == TokenTypes.OBJBLOCK) { 1129 isInside = true; 1130 break; 1131 } 1132 } 1133 return isInside; 1134 } 1135 1136 /** 1137 * Checks if 2 AST are similar by their type and text. 1138 * 1139 * @param left The first AST to check. 1140 * @param right The second AST to check. 1141 * @return {@code true} if they are similar. 1142 */ 1143 private static boolean isAstSimilar(DetailAST left, DetailAST right) { 1144 return left.getType() == right.getType() && left.getText().equals(right.getText()); 1145 } 1146 1147 /** An AbstractFrame type. */ 1148 private enum FrameType { 1149 1150 /** Class frame type. */ 1151 CLASS_FRAME, 1152 /** Constructor frame type. */ 1153 CTOR_FRAME, 1154 /** Method frame type. */ 1155 METHOD_FRAME, 1156 /** Block frame type. */ 1157 BLOCK_FRAME, 1158 /** Catch frame type. */ 1159 CATCH_FRAME, 1160 /** For frame type. */ 1161 FOR_FRAME, 1162 /** Try with resources frame type. */ 1163 TRY_WITH_RESOURCES_FRAME 1164 1165 } 1166 1167 /** 1168 * Defines whether a method name is being looked for during a frame lookup. 1169 */ 1170 private enum LookMode { 1171 1172 /** Look for a method name. */ 1173 LOOK_FOR_METHOD, 1174 /** Do not look for a method name. */ 1175 NO_LOOK_FOR_METHOD 1176 1177 } 1178 1179 /** 1180 * A declaration frame. 1181 */ 1182 private abstract static class AbstractFrame { 1183 1184 /** Set of name of variables declared in this frame. */ 1185 private final Set<DetailAST> varIdents; 1186 1187 /** Parent frame. */ 1188 private final AbstractFrame parent; 1189 1190 /** Name identifier token. */ 1191 private final DetailAST frameNameIdent; 1192 1193 /** 1194 * Constructor -- invocable only via super() from subclasses. 1195 * 1196 * @param parent parent frame. 1197 * @param ident frame name ident. 1198 */ 1199 protected AbstractFrame(AbstractFrame parent, DetailAST ident) { 1200 this.parent = parent; 1201 frameNameIdent = ident; 1202 varIdents = new HashSet<>(); 1203 } 1204 1205 /** 1206 * Get the type of the frame. 1207 * 1208 * @return a FrameType. 1209 */ 1210 public abstract FrameType getType(); 1211 1212 /** 1213 * Add a name to the frame. 1214 * 1215 * @param identToAdd the name we're adding. 1216 */ 1217 private void addIdent(DetailAST identToAdd) { 1218 varIdents.add(identToAdd); 1219 } 1220 1221 /** 1222 * Returns the parent frame. 1223 * 1224 * @return the parent frame 1225 */ 1226 public AbstractFrame getParent() { 1227 return parent; 1228 } 1229 1230 /** 1231 * Returns the name identifier text. 1232 * 1233 * @return the name identifier text 1234 */ 1235 public String getFrameName() { 1236 return frameNameIdent.getText(); 1237 } 1238 1239 /** 1240 * Returns the name identifier token. 1241 * 1242 * @return the name identifier token 1243 */ 1244 public DetailAST getFrameNameIdent() { 1245 return frameNameIdent; 1246 } 1247 1248 /** 1249 * Check whether the frame contains a field or a variable with the given name. 1250 * 1251 * @param identToFind the IDENT ast of the name we're looking for. 1252 * @return whether it was found. 1253 */ 1254 public boolean containsFieldOrVariable(DetailAST identToFind) { 1255 return containsFieldOrVariableDef(varIdents, identToFind); 1256 } 1257 1258 /** 1259 * Check whether the frame contains a given name. 1260 * 1261 * @param identToFind IDENT ast of the name we're looking for. 1262 * @param lookMode mode defining whether we are looking for a method name. 1263 * @return whether it was found. 1264 */ 1265 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) { 1266 final AbstractFrame frame; 1267 1268 if (lookMode == LookMode.NO_LOOK_FOR_METHOD 1269 && containsFieldOrVariable(identToFind)) { 1270 frame = this; 1271 } 1272 else { 1273 frame = parent.getIfContains(identToFind, lookMode); 1274 } 1275 return frame; 1276 } 1277 1278 /** 1279 * Whether the set contains a declaration with the text of the specified 1280 * IDENT ast and it is declared in a proper position. 1281 * 1282 * @param set the set of declarations. 1283 * @param ident the specified IDENT ast. 1284 * @return true if the set contains a declaration with the text of the specified 1285 * IDENT ast and it is declared in a proper position. 1286 */ 1287 public boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) { 1288 boolean result = false; 1289 for (DetailAST ast: set) { 1290 if (isProperDefinition(ident, ast)) { 1291 result = true; 1292 break; 1293 } 1294 } 1295 return result; 1296 } 1297 1298 /** 1299 * Whether the definition is correspondent to the IDENT. 1300 * 1301 * @param ident the IDENT ast to check. 1302 * @param ast the IDENT ast of the definition to check. 1303 * @return true if ast is correspondent to ident. 1304 */ 1305 public boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1306 final String identToFind = ident.getText(); 1307 return identToFind.equals(ast.getText()) 1308 && CheckUtil.isBeforeInSource(ast, ident); 1309 } 1310 } 1311 1312 /** 1313 * A frame initiated at method definition; holds a method definition token. 1314 */ 1315 private static class MethodFrame extends AbstractFrame { 1316 1317 /** 1318 * Creates method frame. 1319 * 1320 * @param parent parent frame. 1321 * @param ident method name identifier token. 1322 */ 1323 /* package */ MethodFrame(AbstractFrame parent, DetailAST ident) { 1324 super(parent, ident); 1325 } 1326 1327 @Override 1328 public FrameType getType() { 1329 return FrameType.METHOD_FRAME; 1330 } 1331 1332 } 1333 1334 /** 1335 * A frame initiated at constructor definition. 1336 */ 1337 private static class ConstructorFrame extends AbstractFrame { 1338 1339 /** 1340 * Creates a constructor frame. 1341 * 1342 * @param parent parent frame. 1343 * @param ident frame name ident. 1344 */ 1345 /* package */ ConstructorFrame(AbstractFrame parent, DetailAST ident) { 1346 super(parent, ident); 1347 } 1348 1349 @Override 1350 public FrameType getType() { 1351 return FrameType.CTOR_FRAME; 1352 } 1353 1354 } 1355 1356 /** 1357 * A frame initiated at class, enum or interface definition; holds instance variable names. 1358 */ 1359 private static class ClassFrame extends AbstractFrame { 1360 1361 /** Set of idents of instance members declared in this frame. */ 1362 private final Set<DetailAST> instanceMembers; 1363 /** Set of idents of instance methods declared in this frame. */ 1364 private final Set<DetailAST> instanceMethods; 1365 /** Set of idents of variables declared in this frame. */ 1366 private final Set<DetailAST> staticMembers; 1367 /** Set of idents of static methods declared in this frame. */ 1368 private final Set<DetailAST> staticMethods; 1369 1370 /** 1371 * Creates new instance of ClassFrame. 1372 * 1373 * @param parent parent frame. 1374 * @param ident frame name ident. 1375 */ 1376 private ClassFrame(AbstractFrame parent, DetailAST ident) { 1377 super(parent, ident); 1378 instanceMembers = new HashSet<>(); 1379 instanceMethods = new HashSet<>(); 1380 staticMembers = new HashSet<>(); 1381 staticMethods = new HashSet<>(); 1382 } 1383 1384 @Override 1385 public FrameType getType() { 1386 return FrameType.CLASS_FRAME; 1387 } 1388 1389 /** 1390 * Adds static member's ident. 1391 * 1392 * @param ident an ident of static member of the class. 1393 */ 1394 /* package */ void addStaticMember(final DetailAST ident) { 1395 staticMembers.add(ident); 1396 } 1397 1398 /** 1399 * Adds static method's name. 1400 * 1401 * @param ident an ident of static method of the class. 1402 */ 1403 /* package */ void addStaticMethod(final DetailAST ident) { 1404 staticMethods.add(ident); 1405 } 1406 1407 /** 1408 * Adds instance member's ident. 1409 * 1410 * @param ident an ident of instance member of the class. 1411 */ 1412 /* package */ void addInstanceMember(final DetailAST ident) { 1413 instanceMembers.add(ident); 1414 } 1415 1416 /** 1417 * Adds instance method's name. 1418 * 1419 * @param ident an ident of instance method of the class. 1420 */ 1421 /* package */ void addInstanceMethod(final DetailAST ident) { 1422 instanceMethods.add(ident); 1423 } 1424 1425 /** 1426 * Checks if a given name is a known instance member of the class. 1427 * 1428 * @param ident the IDENT ast of the name to check. 1429 * @return true is the given name is a name of a known 1430 * instance member of the class. 1431 */ 1432 /* package */ boolean hasInstanceMember(final DetailAST ident) { 1433 return containsFieldOrVariableDef(instanceMembers, ident); 1434 } 1435 1436 /** 1437 * Checks if a given name is a known instance method of the class. 1438 * 1439 * @param ident the IDENT ast of the method call to check. 1440 * @return true if the given ast is correspondent to a known 1441 * instance method of the class. 1442 */ 1443 /* package */ boolean hasInstanceMethod(final DetailAST ident) { 1444 return containsMethodDef(instanceMethods, ident); 1445 } 1446 1447 /** 1448 * Checks if a given name is a known static method of the class. 1449 * 1450 * @param ident the IDENT ast of the method call to check. 1451 * @return true is the given ast is correspondent to a known 1452 * instance method of the class. 1453 */ 1454 /* package */ boolean hasStaticMethod(final DetailAST ident) { 1455 return containsMethodDef(staticMethods, ident); 1456 } 1457 1458 /** 1459 * Checks whether given instance member has final modifier. 1460 * 1461 * @param instanceMember an instance member of a class. 1462 * @return true if given instance member has final modifier. 1463 */ 1464 /* package */ boolean hasFinalField(final DetailAST instanceMember) { 1465 boolean result = false; 1466 for (DetailAST member : instanceMembers) { 1467 final DetailAST parent = member.getParent(); 1468 if (parent.getType() == TokenTypes.RECORD_COMPONENT_DEF) { 1469 result = true; 1470 } 1471 else { 1472 final DetailAST mods = parent.findFirstToken(TokenTypes.MODIFIERS); 1473 final boolean finalMod = mods.findFirstToken(TokenTypes.FINAL) != null; 1474 if (finalMod && isAstSimilar(member, instanceMember)) { 1475 result = true; 1476 } 1477 } 1478 } 1479 return result; 1480 } 1481 1482 @Override 1483 public boolean containsFieldOrVariable(DetailAST identToFind) { 1484 return containsFieldOrVariableDef(instanceMembers, identToFind) 1485 || containsFieldOrVariableDef(staticMembers, identToFind); 1486 } 1487 1488 @Override 1489 public boolean isProperDefinition(DetailAST ident, DetailAST ast) { 1490 final String identToFind = ident.getText(); 1491 return identToFind.equals(ast.getText()); 1492 } 1493 1494 /** 1495 * Check whether the frame contains a given name. 1496 * 1497 * @param identToFind IDENT ast of the name we're looking for. 1498 * @param lookMode mode defining whether we are looking for a method name. 1499 * @return whether it was found. 1500 */ 1501 @Override 1502 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) { 1503 AbstractFrame frame = null; 1504 1505 if (containsMethod(identToFind) 1506 || containsFieldOrVariable(identToFind)) { 1507 frame = this; 1508 } 1509 else if (getParent() != null) { 1510 frame = getParent().getIfContains(identToFind, lookMode); 1511 } 1512 return frame; 1513 } 1514 1515 /** 1516 * Check whether the frame contains a given method. 1517 * 1518 * @param methodToFind the AST of the method to find. 1519 * @return true, if a method with the same name and number of parameters is found. 1520 */ 1521 private boolean containsMethod(DetailAST methodToFind) { 1522 return containsMethodDef(instanceMethods, methodToFind) 1523 || containsMethodDef(staticMethods, methodToFind); 1524 } 1525 1526 /** 1527 * Whether the set contains a method definition with the 1528 * same name and number of parameters. 1529 * 1530 * @param set the set of definitions. 1531 * @param ident the specified method call IDENT ast. 1532 * @return true if the set contains a definition with the 1533 * same name and number of parameters. 1534 */ 1535 private static boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) { 1536 boolean result = false; 1537 for (DetailAST ast: set) { 1538 if (isSimilarSignature(ident, ast)) { 1539 result = true; 1540 break; 1541 } 1542 } 1543 return result; 1544 } 1545 1546 /** 1547 * Whether the method definition has the same name and number of parameters. 1548 * 1549 * @param ident the specified method call IDENT ast. 1550 * @param ast the ast of a method definition to compare with. 1551 * @return true if a method definition has the same name and number of parameters 1552 * as the method call. 1553 */ 1554 private static boolean isSimilarSignature(DetailAST ident, DetailAST ast) { 1555 boolean result = false; 1556 final DetailAST elistToken = ident.getParent().findFirstToken(TokenTypes.ELIST); 1557 if (elistToken != null && ident.getText().equals(ast.getText())) { 1558 final int paramsNumber = 1559 ast.getParent().findFirstToken(TokenTypes.PARAMETERS).getChildCount(); 1560 final int argsNumber = elistToken.getChildCount(); 1561 result = paramsNumber == argsNumber; 1562 } 1563 return result; 1564 } 1565 1566 } 1567 1568 /** 1569 * An anonymous class frame; holds instance variable names. 1570 */ 1571 private static class AnonymousClassFrame extends ClassFrame { 1572 1573 /** The name of the frame. */ 1574 private final String frameName; 1575 1576 /** 1577 * Creates anonymous class frame. 1578 * 1579 * @param parent parent frame. 1580 * @param frameName name of the frame. 1581 */ 1582 /* package */ AnonymousClassFrame(AbstractFrame parent, String frameName) { 1583 super(parent, null); 1584 this.frameName = frameName; 1585 } 1586 1587 @Override 1588 public String getFrameName() { 1589 return frameName; 1590 } 1591 1592 } 1593 1594 /** 1595 * A frame for the implicit class of a compact source file (JEP 512). 1596 * The class is unnamed in source code, so a reference to one of its members 1597 * from a nested class cannot be qualified with {@code <ClassName>.this} and is 1598 * not flagged, the same way references to anonymous class members are skipped. 1599 */ 1600 private static class CompactCompilationUnitFrame extends ClassFrame { 1601 1602 /** 1603 * Creates compact compilation unit frame. 1604 * 1605 * @param parent parent frame. 1606 * @param ident frame name ident. 1607 */ 1608 /* package */ CompactCompilationUnitFrame(AbstractFrame parent, DetailAST ident) { 1609 super(parent, ident); 1610 } 1611 1612 } 1613 1614 /** 1615 * A frame initiated on entering a statement list; holds local variable names. 1616 */ 1617 private static class BlockFrame extends AbstractFrame { 1618 1619 /** 1620 * Creates block frame. 1621 * 1622 * @param parent parent frame. 1623 * @param ident ident frame name ident. 1624 */ 1625 /* package */ BlockFrame(AbstractFrame parent, DetailAST ident) { 1626 super(parent, ident); 1627 } 1628 1629 @Override 1630 public FrameType getType() { 1631 return FrameType.BLOCK_FRAME; 1632 } 1633 1634 } 1635 1636 /** 1637 * A frame initiated on entering a catch block; holds local catch variable names. 1638 */ 1639 private static class CatchFrame extends AbstractFrame { 1640 1641 /** 1642 * Creates catch frame. 1643 * 1644 * @param parent parent frame. 1645 * @param ident ident frame name ident. 1646 */ 1647 /* package */ CatchFrame(AbstractFrame parent, DetailAST ident) { 1648 super(parent, ident); 1649 } 1650 1651 @Override 1652 public FrameType getType() { 1653 return FrameType.CATCH_FRAME; 1654 } 1655 1656 /** 1657 * Check whether the frame contains a given name. 1658 * 1659 * @param identToFind IDENT ast of the name we're looking for. 1660 * @param lookMode mode defining whether we are looking for a method name. 1661 * @return whether it was found. 1662 */ 1663 @Override 1664 public AbstractFrame getIfContains(DetailAST identToFind, LookMode lookMode) { 1665 final AbstractFrame frame; 1666 1667 if (lookMode == LookMode.NO_LOOK_FOR_METHOD 1668 && containsFieldOrVariable(identToFind)) { 1669 frame = this; 1670 } 1671 else if (getParent().getType() == FrameType.TRY_WITH_RESOURCES_FRAME) { 1672 // Skip try-with-resources frame because resources cannot be accessed from catch 1673 frame = getParent().getParent().getIfContains(identToFind, lookMode); 1674 } 1675 else { 1676 frame = getParent().getIfContains(identToFind, lookMode); 1677 } 1678 return frame; 1679 } 1680 1681 } 1682 1683 /** 1684 * A frame initiated on entering a for block; holds local for variable names. 1685 */ 1686 private static class ForFrame extends AbstractFrame { 1687 1688 /** 1689 * Creates for frame. 1690 * 1691 * @param parent parent frame. 1692 * @param ident ident frame name ident. 1693 */ 1694 /* package */ ForFrame(AbstractFrame parent, DetailAST ident) { 1695 super(parent, ident); 1696 } 1697 1698 @Override 1699 public FrameType getType() { 1700 return FrameType.FOR_FRAME; 1701 } 1702 1703 } 1704 1705 /** 1706 * A frame initiated on entering a try-with-resources construct; 1707 * holds local resources for the try block. 1708 */ 1709 private static class TryWithResourcesFrame extends AbstractFrame { 1710 1711 /** 1712 * Creates try-with-resources frame. 1713 * 1714 * @param parent parent frame. 1715 * @param ident ident frame name ident. 1716 */ 1717 /* package */ TryWithResourcesFrame(AbstractFrame parent, DetailAST ident) { 1718 super(parent, ident); 1719 } 1720 1721 @Override 1722 public FrameType getType() { 1723 return FrameType.TRY_WITH_RESOURCES_FRAME; 1724 } 1725 1726 } 1727 1728}