001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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.Deque; 023import java.util.Map; 024import java.util.Queue; 025import java.util.Set; 026 027import com.google.common.collect.ImmutableSet; 028import com.google.common.collect.Lists; 029import com.google.common.collect.Maps; 030import com.google.common.collect.Sets; 031import com.puppycrawl.tools.checkstyle.api.Check; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.TokenTypes; 034import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 035 036/** 037 * <p>Checks that code doesn't rely on the "this" default. 038 * That is references to instance variables and methods of the present 039 * object are explicitly of the form "this.varName" or 040 * "this.methodName(args)". 041 *</p> 042 * 043 * <p>Examples of use: 044 * <pre> 045 * <module name="RequireThis"/> 046 * </pre> 047 * An example of how to configure to check {@code this} qualifier for 048 * methods only: 049 * <pre> 050 * <module name="RequireThis"> 051 * <property name="checkFields" value="false"/> 052 * <property name="checkMethods" value="true"/> 053 * </module> 054 * </pre> 055 * 056 * <p>Limitations: I'm not currently doing anything about static variables 057 * or catch-blocks. Static methods invoked on a class name seem to be OK; 058 * both the class name and the method name have a DOT parent. 059 * Non-static methods invoked on either this or a variable name seem to be 060 * OK, likewise.</p> 061 * <p>Much of the code for this check was cribbed from Rick Giles's 062 * {@code HiddenFieldCheck}.</p> 063 * 064 * @author Stephen Bloch 065 * @author o_sukhodolsky 066 */ 067public class RequireThisCheck extends Check { 068 069 /** 070 * A key is pointing to the warning message text in "messages.properties" 071 * file. 072 */ 073 public static final String MSG_METHOD = "require.this.method"; 074 075 /** 076 * A key is pointing to the warning message text in "messages.properties" 077 * file. 078 */ 079 public static final String MSG_VARIABLE = "require.this.variable"; 080 081 /** 082 * Set of all declaration tokens. 083 */ 084 private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of( 085 TokenTypes.VARIABLE_DEF, 086 TokenTypes.CTOR_DEF, 087 TokenTypes.METHOD_DEF, 088 TokenTypes.CLASS_DEF, 089 TokenTypes.ENUM_DEF, 090 TokenTypes.INTERFACE_DEF, 091 TokenTypes.PARAMETER_DEF, 092 TokenTypes.TYPE_ARGUMENT 093 ); 094 095 /** 096 * Tree of all the parsed frames. 097 */ 098 private Map<DetailAST, LexicalFrame> frames; 099 100 /** 101 * Frame for the currently processed AST. 102 */ 103 private LexicalFrame current; 104 105 /** Whether we should check fields usage. */ 106 private boolean checkFields = true; 107 /** Whether we should check methods usage. */ 108 private boolean checkMethods = true; 109 110 /** 111 * Setter for checkFields property. 112 * @param checkFields should we check fields usage or not. 113 */ 114 public void setCheckFields(boolean checkFields) { 115 this.checkFields = checkFields; 116 } 117 118 /** 119 * Setter for checkMethods property. 120 * @param checkMethods should we check methods usage or not. 121 */ 122 public void setCheckMethods(boolean checkMethods) { 123 this.checkMethods = checkMethods; 124 } 125 126 @Override 127 public int[] getDefaultTokens() { 128 return getAcceptableTokens(); 129 } 130 131 @Override 132 public int[] getRequiredTokens() { 133 return getAcceptableTokens(); 134 } 135 136 @Override 137 public int[] getAcceptableTokens() { 138 return new int[] { 139 TokenTypes.CLASS_DEF, 140 TokenTypes.INTERFACE_DEF, 141 TokenTypes.ENUM_DEF, 142 TokenTypes.CTOR_DEF, 143 TokenTypes.METHOD_DEF, 144 TokenTypes.SLIST, 145 TokenTypes.IDENT, 146 }; 147 } 148 149 @Override 150 public void beginTree(DetailAST rootAST) { 151 final Deque<LexicalFrame> frameStack = Lists.newLinkedList(); 152 frameStack.add(new GlobalFrame()); 153 154 frames = Maps.newHashMap(); 155 156 DetailAST curNode = rootAST; 157 while (curNode != null) { 158 collectDeclarations(frameStack, curNode); 159 DetailAST toVisit = curNode.getFirstChild(); 160 while (curNode != null && toVisit == null) { 161 endCollectingDeclarations(frameStack, curNode); 162 toVisit = curNode.getNextSibling(); 163 if (toVisit == null) { 164 curNode = curNode.getParent(); 165 } 166 } 167 curNode = toVisit; 168 } 169 } 170 171 @Override 172 public void visitToken(DetailAST ast) { 173 switch (ast.getType()) { 174 case TokenTypes.IDENT : 175 processIdent(ast); 176 break; 177 case TokenTypes.CLASS_DEF : 178 case TokenTypes.INTERFACE_DEF : 179 case TokenTypes.ENUM_DEF : 180 case TokenTypes.ANNOTATION_DEF : 181 case TokenTypes.SLIST : 182 case TokenTypes.METHOD_DEF : 183 case TokenTypes.CTOR_DEF : 184 current = frames.get(ast); 185 break; 186 default : 187 // do nothing 188 } 189 } 190 191 /** 192 * Checks if a given IDENT is method call or field name which 193 * require explicit {@code this} qualifier. 194 * 195 * @param ast IDENT to check. 196 */ 197 private void processIdent(DetailAST ast) { 198 final int parentType = ast.getParent().getType(); 199 switch (parentType) { 200 case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR: 201 case TokenTypes.ANNOTATION: 202 case TokenTypes.ANNOTATION_FIELD_DEF: 203 // no need to check annotations content 204 break; 205 case TokenTypes.METHOD_CALL: 206 // let's check method calls 207 if (checkMethods && isClassMethod(ast)) { 208 log(ast, MSG_METHOD, ast.getText()); 209 } 210 break; 211 default: 212 if (checkFields) { 213 processField(ast, parentType); 214 } 215 break; 216 } 217 } 218 219 /** 220 * Process validation of Field. 221 * @param ast field definition ast token 222 * @param parentType type of the parent 223 */ 224 private void processField(DetailAST ast, int parentType) { 225 final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null; 226 final boolean methodNameInMethodCall = parentType == TokenTypes.DOT 227 && ast.getPreviousSibling() != null; 228 final boolean typeName = parentType == TokenTypes.TYPE 229 || parentType == TokenTypes.LITERAL_NEW; 230 231 if (!importOrPackage 232 && !methodNameInMethodCall 233 && !typeName 234 && !isDeclarationToken(parentType) 235 && isClassField(ast)) { 236 log(ast, MSG_VARIABLE, ast.getText()); 237 } 238 } 239 240 /** 241 * Parse the next AST for declarations. 242 * 243 * @param frameStack Stack containing the FrameTree being built 244 * @param ast AST to parse 245 */ 246 private static void collectDeclarations(Deque<LexicalFrame> frameStack, 247 DetailAST ast) { 248 final LexicalFrame frame = frameStack.peek(); 249 switch (ast.getType()) { 250 case TokenTypes.VARIABLE_DEF : 251 collectVariableDeclarations(ast, frame); 252 break; 253 case TokenTypes.PARAMETER_DEF : 254 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT); 255 frame.addIdent(parameterIdent); 256 break; 257 case TokenTypes.CLASS_DEF : 258 case TokenTypes.INTERFACE_DEF : 259 case TokenTypes.ENUM_DEF : 260 case TokenTypes.ANNOTATION_DEF : 261 final DetailAST classIdent = ast.findFirstToken(TokenTypes.IDENT); 262 frame.addIdent(classIdent); 263 frameStack.addFirst(new ClassFrame(frame)); 264 break; 265 case TokenTypes.SLIST : 266 frameStack.addFirst(new BlockFrame(frame)); 267 break; 268 case TokenTypes.METHOD_DEF : 269 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 270 if (frame instanceof ClassFrame) { 271 final DetailAST mods = 272 ast.findFirstToken(TokenTypes.MODIFIERS); 273 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 274 ((ClassFrame) frame).addStaticMethod(ident); 275 } 276 else { 277 ((ClassFrame) frame).addInstanceMethod(ident); 278 } 279 } 280 frameStack.addFirst(new MethodFrame(frame)); 281 break; 282 case TokenTypes.CTOR_DEF : 283 frameStack.addFirst(new MethodFrame(frame)); 284 break; 285 default: 286 // do nothing 287 } 288 } 289 290 /** 291 * Collect Variable Declarations. 292 * @param ast variable token 293 * @param frame current frame 294 */ 295 private static void collectVariableDeclarations(DetailAST ast, LexicalFrame frame) { 296 final DetailAST ident = 297 ast.findFirstToken(TokenTypes.IDENT); 298 if (frame instanceof ClassFrame) { 299 final DetailAST mods = 300 ast.findFirstToken(TokenTypes.MODIFIERS); 301 if (ScopeUtils.isInInterfaceBlock(ast) 302 || mods.branchContains(TokenTypes.LITERAL_STATIC)) { 303 ((ClassFrame) frame).addStaticMember(ident); 304 } 305 else { 306 ((ClassFrame) frame).addInstanceMember(ident); 307 } 308 } 309 else { 310 frame.addIdent(ident); 311 } 312 } 313 314 /** 315 * End parsing of the AST for declarations. 316 * 317 * @param frameStack Stack containing the FrameTree being built 318 * @param ast AST that was parsed 319 */ 320 private void endCollectingDeclarations(Queue<LexicalFrame> frameStack, 321 DetailAST ast) { 322 switch (ast.getType()) { 323 case TokenTypes.CLASS_DEF : 324 case TokenTypes.INTERFACE_DEF : 325 case TokenTypes.ENUM_DEF : 326 case TokenTypes.ANNOTATION_DEF : 327 case TokenTypes.SLIST : 328 case TokenTypes.METHOD_DEF : 329 case TokenTypes.CTOR_DEF : 330 frames.put(ast, frameStack.poll()); 331 break; 332 default : 333 // do nothing 334 } 335 } 336 337 /** 338 * Check if given name is a name for class field in current environment. 339 * @param ident an IDENT ast to check 340 * @return true is the given name is name of member. 341 */ 342 private boolean isClassField(DetailAST ident) { 343 final LexicalFrame frame = findFrame(ident, false); 344 return frame instanceof ClassFrame 345 && ((ClassFrame) frame).hasInstanceMember(ident); 346 } 347 348 /** 349 * Check if given name is a name for class method in current environment. 350 * @param ident the IDENT ast of the name to check 351 * @return true is the given name is name of method. 352 */ 353 private boolean isClassMethod(DetailAST ident) { 354 final LexicalFrame frame = findFrame(ident, true); 355 return frame instanceof ClassFrame 356 && ((ClassFrame) frame).hasInstanceMethod(ident); 357 } 358 359 /** 360 * Find frame containing declaration. 361 * @param name IDENT ast of the declaration to find. 362 * @param lookForMethod whether we are looking for a method name. 363 * @return LexicalFrame containing declaration or null. 364 */ 365 private LexicalFrame findFrame(DetailAST name, boolean lookForMethod) { 366 if (current == null) { 367 return null; 368 } 369 else { 370 return current.getIfContains(name, lookForMethod); 371 } 372 } 373 374 /** 375 * Check that token is related to Definition tokens. 376 * @param parentType token Type 377 * @return true if token is related to Definition Tokens 378 */ 379 private static boolean isDeclarationToken(int parentType) { 380 return DECLARATION_TOKENS.contains(parentType); 381 } 382 383 /** 384 * A declaration frame. 385 * @author Stephen Bloch 386 */ 387 private static class LexicalFrame { 388 /** Set of name of variables declared in this frame. */ 389 private final Set<DetailAST> varIdents; 390 391 /** 392 * Parent frame. 393 */ 394 private final LexicalFrame parent; 395 396 /** 397 * Constructor -- invokable only via super() from subclasses. 398 * 399 * @param parent parent frame 400 */ 401 protected LexicalFrame(LexicalFrame parent) { 402 this.parent = parent; 403 varIdents = Sets.newHashSet(); 404 } 405 406 /** 407 * Add a name to the frame. 408 * @param identToAdd the name we're adding 409 */ 410 private void addIdent(DetailAST identToAdd) { 411 varIdents.add(identToAdd); 412 } 413 414 protected LexicalFrame getParent() { 415 return parent; 416 } 417 418 /** Check whether the frame contains a given name. 419 * @param nameToFind the IDENT ast of the name we're looking for 420 * @return whether it was found 421 */ 422 boolean contains(DetailAST nameToFind) { 423 return containsName(varIdents, nameToFind); 424 } 425 426 /** Check whether the frame contains a given name. 427 * @param nameToFind IDENT ast of the name we're looking for. 428 * @param lookForMethod whether we are looking for a method name. 429 * @return whether it was found. 430 */ 431 protected LexicalFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 432 LexicalFrame frame = null; 433 434 if (!lookForMethod 435 && contains(nameToFind)) { 436 frame = this; 437 } 438 else if (parent != null) { 439 frame = parent.getIfContains(nameToFind, lookForMethod); 440 } 441 return frame; 442 } 443 444 /** 445 * Whether the set contains a declaration with the text of the specified 446 * IDENT ast and it is declared in a proper position. 447 * @param set the set of declarations. 448 * @param ident the specified IDENT ast 449 * @return true if the set contains a declaration with the text of the specified 450 * IDENT ast and it is declared in a proper position. 451 */ 452 protected boolean containsName(Set<DetailAST> set, DetailAST ident) { 453 boolean result = false; 454 for (DetailAST ast: set) { 455 if (isProperDefinition(ident, ast)) { 456 result = true; 457 break; 458 } 459 } 460 return result; 461 } 462 463 /** 464 * Whether the definition is correspondent to the IDENT. 465 * @param ident the IDENT ast to check. 466 * @param ast the IDENT ast of the definition to check. 467 * @return true if ast is correspondent to ident. 468 */ 469 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 470 final String nameToFind = ident.getText(); 471 return nameToFind.equals(ast.getText()) 472 && checkPosition(ast, ident); 473 } 474 475 /** 476 * Whether the declaration is located before the checked ast. 477 * @param ast1 the IDENT ast of the declaration. 478 * @param ast2 the IDENT ast to check. 479 * @return true, if the declaration is located before the checked ast. 480 */ 481 private static boolean checkPosition(DetailAST ast1, DetailAST ast2) { 482 boolean result = false; 483 if (ast1.getLineNo() < ast2.getLineNo() 484 || ast1.getLineNo() == ast2.getLineNo() 485 && ast1.getColumnNo() < ast2.getColumnNo()) { 486 result = true; 487 } 488 return result; 489 } 490 } 491 492 /** 493 * The global frame; should hold only class names. 494 * @author Stephen Bloch 495 */ 496 private static class GlobalFrame extends LexicalFrame { 497 498 /** 499 * Constructor for the root of the FrameTree. 500 */ 501 protected GlobalFrame() { 502 super(null); 503 } 504 } 505 506 /** 507 * A frame initiated at method definition; holds parameter names. 508 * @author Stephen Bloch 509 */ 510 private static class MethodFrame extends LexicalFrame { 511 /** 512 * Creates method frame. 513 * @param parent parent frame 514 */ 515 protected MethodFrame(LexicalFrame parent) { 516 super(parent); 517 } 518 } 519 520 /** 521 * A frame initiated at class definition; holds instance variable 522 * names. For the present, I'm not worried about other class names, 523 * method names, etc. 524 * @author Stephen Bloch 525 */ 526 private static class ClassFrame extends LexicalFrame { 527 /** Set of idents of instance members declared in this frame. */ 528 private final Set<DetailAST> instanceMembers; 529 /** Set of idents of instance methods declared in this frame. */ 530 private final Set<DetailAST> instanceMethods; 531 /** Set of idents of variables declared in this frame. */ 532 private final Set<DetailAST> staticMembers; 533 /** Set of idents of static methods declared in this frame. */ 534 private final Set<DetailAST> staticMethods; 535 536 /** 537 * Creates new instance of ClassFrame. 538 * @param parent parent frame 539 */ 540 ClassFrame(LexicalFrame parent) { 541 super(parent); 542 instanceMembers = Sets.newHashSet(); 543 instanceMethods = Sets.newHashSet(); 544 staticMembers = Sets.newHashSet(); 545 staticMethods = Sets.newHashSet(); 546 } 547 548 /** 549 * Adds static member's ident. 550 * @param ident an ident of static member of the class 551 */ 552 public void addStaticMember(final DetailAST ident) { 553 staticMembers.add(ident); 554 } 555 556 /** 557 * Adds static method's name. 558 * @param ident an ident of static method of the class 559 */ 560 public void addStaticMethod(final DetailAST ident) { 561 staticMethods.add(ident); 562 } 563 564 /** 565 * Adds instance member's ident. 566 * @param ident an ident of instance member of the class 567 */ 568 public void addInstanceMember(final DetailAST ident) { 569 instanceMembers.add(ident); 570 } 571 572 /** 573 * Adds instance method's name. 574 * @param ident an ident of instance method of the class 575 */ 576 public void addInstanceMethod(final DetailAST ident) { 577 instanceMethods.add(ident); 578 } 579 580 /** 581 * Checks if a given name is a known instance member of the class. 582 * @param ident the IDENT ast of the name to check 583 * @return true is the given name is a name of a known 584 * instance member of the class 585 */ 586 public boolean hasInstanceMember(final DetailAST ident) { 587 return containsName(instanceMembers, ident); 588 } 589 590 /** 591 * Checks if a given name is a known instance method of the class. 592 * @param ident the IDENT ast of the name to check 593 * @return true is the given name is a name of a known 594 * instance method of the class 595 */ 596 public boolean hasInstanceMethod(final DetailAST ident) { 597 return containsName(instanceMethods, ident); 598 } 599 600 @Override 601 boolean contains(DetailAST nameToFind) { 602 return super.contains(nameToFind) 603 || containsName(instanceMembers, nameToFind) 604 || containsName(instanceMethods, nameToFind) 605 || containsName(staticMembers, nameToFind) 606 || containsName(staticMethods, nameToFind); 607 } 608 609 @Override 610 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 611 final String nameToFind = ident.getText(); 612 return nameToFind.equals(ast.getText()); 613 } 614 615 @Override 616 protected LexicalFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 617 LexicalFrame frame; 618 619 if (contains(nameToFind)) { 620 frame = this; 621 } 622 else { 623 frame = getParent().getIfContains(nameToFind, lookForMethod); 624 } 625 return frame; 626 } 627 } 628 629 /** 630 * A frame initiated on entering a statement list; holds local variable 631 * names. For the present, I'm not worried about other class names, 632 * method names, etc. 633 * @author Stephen Bloch 634 */ 635 private static class BlockFrame extends LexicalFrame { 636 637 /** 638 * Creates block frame. 639 * @param parent parent frame 640 */ 641 protected BlockFrame(LexicalFrame parent) { 642 super(parent); 643 } 644 } 645}