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