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