Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.AbstractSuperCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractSuperCheck
100%
57/57
100%
40/40
2.062
AbstractSuperCheck$MethodNode
100%
8/8
N/A
2.062
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks.coding;
 21  
 
 22  
 import java.util.Deque;
 23  
 import java.util.LinkedList;
 24  
 
 25  
 import antlr.collections.AST;
 26  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 27  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 29  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 30  
 import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
 31  
 
 32  
 /**
 33  
  * <p>
 34  
  * Abstract class for checking that an overriding method with no parameters
 35  
  * invokes the super method.
 36  
  * </p>
 37  
  * @author Rick Giles
 38  
  */
 39  
 @FileStatefulCheck
 40  19
 public abstract class AbstractSuperCheck
 41  
         extends AbstractCheck {
 42  
 
 43  
     /**
 44  
      * A key is pointing to the warning message text in "messages.properties"
 45  
      * file.
 46  
      */
 47  
     public static final String MSG_KEY = "missing.super.call";
 48  
 
 49  
     /** Stack of methods. */
 50  19
     private final Deque<MethodNode> methodStack = new LinkedList<>();
 51  
 
 52  
     /**
 53  
      * Returns the name of the overriding method.
 54  
      * @return the name of the overriding method.
 55  
      */
 56  
     protected abstract String getMethodName();
 57  
 
 58  
     @Override
 59  
     public int[] getAcceptableTokens() {
 60  9
         return getRequiredTokens();
 61  
     }
 62  
 
 63  
     @Override
 64  
     public int[] getDefaultTokens() {
 65  23
         return getRequiredTokens();
 66  
     }
 67  
 
 68  
     @Override
 69  
     public int[] getRequiredTokens() {
 70  55
         return new int[] {
 71  
             TokenTypes.METHOD_DEF,
 72  
             TokenTypes.LITERAL_SUPER,
 73  
         };
 74  
     }
 75  
 
 76  
     @Override
 77  
     public void beginTree(DetailAST rootAST) {
 78  7
         methodStack.clear();
 79  7
     }
 80  
 
 81  
     @Override
 82  
     public void visitToken(DetailAST ast) {
 83  46
         if (isOverridingMethod(ast)) {
 84  13
             methodStack.add(new MethodNode(ast));
 85  
         }
 86  33
         else if (isSuperCall(ast)) {
 87  6
             final MethodNode methodNode = methodStack.getLast();
 88  6
             methodNode.setCallingSuper();
 89  
         }
 90  46
     }
 91  
 
 92  
     /**
 93  
      * Determines whether a 'super' literal is a call to the super method
 94  
      * for this check.
 95  
      * @param literalSuperAst the AST node of a 'super' literal.
 96  
      * @return true if ast is a call to the super method for this check.
 97  
      */
 98  
     private boolean isSuperCall(DetailAST literalSuperAst) {
 99  33
         boolean superCall = false;
 100  
 
 101  33
         if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) {
 102  
             // dot operator?
 103  19
             final DetailAST dotAst = literalSuperAst.getParent();
 104  
 
 105  19
             if (!isSameNameMethod(literalSuperAst)
 106  16
                 && !hasArguments(dotAst)) {
 107  14
                 superCall = isSuperCallInOverridingMethod(dotAst);
 108  
             }
 109  
         }
 110  33
         return superCall;
 111  
     }
 112  
 
 113  
     /**
 114  
      * Determines whether a super call in overriding method.
 115  
      *
 116  
      * @param ast The AST node of a 'dot operator' in 'super' call.
 117  
      * @return true if super call in overriding method.
 118  
      */
 119  
     private boolean isSuperCallInOverridingMethod(DetailAST ast) {
 120  14
         boolean inOverridingMethod = false;
 121  14
         DetailAST dotAst = ast;
 122  
 
 123  76
         while (dotAst.getType() != TokenTypes.CTOR_DEF
 124  74
                 && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
 125  
 
 126  72
             if (dotAst.getType() == TokenTypes.METHOD_DEF) {
 127  10
                 inOverridingMethod = isOverridingMethod(dotAst);
 128  10
                 break;
 129  
             }
 130  62
             dotAst = dotAst.getParent();
 131  
 
 132  
         }
 133  14
         return inOverridingMethod;
 134  
     }
 135  
 
 136  
     /**
 137  
      * Does method have any arguments.
 138  
      * @param methodCallDotAst DOT DetailAST
 139  
      * @return true if any parameters found
 140  
      */
 141  
     private static boolean hasArguments(DetailAST methodCallDotAst) {
 142  16
         final DetailAST argumentsList = methodCallDotAst.getNextSibling();
 143  16
         return argumentsList.getChildCount() > 0;
 144  
     }
 145  
 
 146  
     /**
 147  
      * Is same name of method.
 148  
      * @param ast method AST
 149  
      * @return true if method name is the same
 150  
      */
 151  
     private boolean isSameNameMethod(DetailAST ast) {
 152  
 
 153  19
         AST sibling = ast.getNextSibling();
 154  
         // ignore type parameters
 155  19
         if (sibling != null
 156  18
             && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
 157  1
             sibling = sibling.getNextSibling();
 158  
         }
 159  19
         return sibling == null || !getMethodName().equals(sibling.getText());
 160  
     }
 161  
 
 162  
     @Override
 163  
     public void leaveToken(DetailAST ast) {
 164  45
         if (isOverridingMethod(ast)) {
 165  12
             final MethodNode methodNode =
 166  12
                 methodStack.removeLast();
 167  12
             if (!methodNode.isCallingSuper()) {
 168  6
                 final DetailAST methodAST = methodNode.getMethod();
 169  6
                 final DetailAST nameAST =
 170  6
                     methodAST.findFirstToken(TokenTypes.IDENT);
 171  12
                 log(nameAST.getLineNo(), nameAST.getColumnNo(),
 172  6
                     MSG_KEY, nameAST.getText());
 173  
             }
 174  
         }
 175  45
     }
 176  
 
 177  
     /**
 178  
      * Determines whether an AST is a method definition for this check,
 179  
      * with 0 parameters.
 180  
      * @param ast the method definition AST.
 181  
      * @return true if the method of ast is a method for this check.
 182  
      */
 183  
     private boolean isOverridingMethod(DetailAST ast) {
 184  101
         boolean overridingMethod = false;
 185  
 
 186  101
         if (ast.getType() == TokenTypes.METHOD_DEF
 187  63
                 && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
 188  61
             final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
 189  61
             final String name = nameAST.getText();
 190  61
             final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
 191  
 
 192  61
             if (getMethodName().equals(name)
 193  41
                     && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
 194  37
                 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
 195  37
                 overridingMethod = params.getChildCount() == 0;
 196  
             }
 197  
         }
 198  101
         return overridingMethod;
 199  
     }
 200  
 
 201  
     /**
 202  
      * Stack node for a method definition and a record of
 203  
      * whether the method has a call to the super method.
 204  
      * @author Rick Giles
 205  
      */
 206  
     private static class MethodNode {
 207  
         /** Method definition. */
 208  
         private final DetailAST method;
 209  
 
 210  
         /** True if the overriding method calls the super method. */
 211  
         private boolean callingSuper;
 212  
 
 213  
         /**
 214  
          * Constructs a stack node for a method definition.
 215  
          * @param ast AST for the method definition.
 216  
          */
 217  13
         MethodNode(DetailAST ast) {
 218  13
             method = ast;
 219  13
             callingSuper = false;
 220  13
         }
 221  
 
 222  
         /**
 223  
          * Records that the overriding method has a call to the super method.
 224  
          */
 225  
         public void setCallingSuper() {
 226  6
             callingSuper = true;
 227  6
         }
 228  
 
 229  
         /**
 230  
          * Determines whether the overriding method has a call to the super
 231  
          * method.
 232  
          * @return true if the overriding method has a call to the super method.
 233  
          */
 234  
         public boolean isCallingSuper() {
 235  12
             return callingSuper;
 236  
         }
 237  
 
 238  
         /**
 239  
          * Returns the overriding method definition AST.
 240  
          * @return the overriding method definition AST.
 241  
          */
 242  
         public DetailAST getMethod() {
 243  6
             return method;
 244  
         }
 245  
     }
 246  
 }