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