Coverage Report - com.puppycrawl.tools.checkstyle.checks.FinalParametersCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
FinalParametersCheck
100%
58/58
100%
26/26
2.3
 
 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;
 21  
 
 22  
 import java.util.Arrays;
 23  
 import java.util.Collections;
 24  
 import java.util.Set;
 25  
 import java.util.stream.Collectors;
 26  
 
 27  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 29  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 30  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 31  
 import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
 32  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 33  
 
 34  
 /**
 35  
  * Check that method/constructor/catch/foreach parameters are final.
 36  
  * The user can set the token set to METHOD_DEF, CONSTRUCTOR_DEF,
 37  
  * LITERAL_CATCH, FOR_EACH_CLAUSE or any combination of these token
 38  
  * types, to control the scope of this check.
 39  
  * Default scope is both METHOD_DEF and CONSTRUCTOR_DEF.
 40  
  * <p>
 41  
  * Check has an option <b>ignorePrimitiveTypes</b> which allows ignoring lack of
 42  
  * final modifier at
 43  
  * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">
 44  
  *  primitive data type</a> parameter. Default value <b>false</b>.
 45  
  * </p>
 46  
  * E.g.:
 47  
  * <p>
 48  
  * {@code
 49  
  * private void foo(int x) { ... } //parameter is of primitive type
 50  
  * }
 51  
  * </p>
 52  
  *
 53  
  * @author lkuehne
 54  
  * @author o_sukhodolsky
 55  
  * @author Michael Studman
 56  
  * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a>
 57  
  */
 58  
 @StatelessCheck
 59  16
 public class FinalParametersCheck extends AbstractCheck {
 60  
 
 61  
     /**
 62  
      * A key is pointing to the warning message text in "messages.properties"
 63  
      * file.
 64  
      */
 65  
     public static final String MSG_KEY = "final.parameter";
 66  
 
 67  
     /**
 68  
      * Contains
 69  
      * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">
 70  
      * primitive datatypes</a>.
 71  
      */
 72  32
     private final Set<Integer> primitiveDataTypes = Collections.unmodifiableSet(
 73  16
         Arrays.stream(new Integer[] {
 74  16
             TokenTypes.LITERAL_BYTE,
 75  16
             TokenTypes.LITERAL_SHORT,
 76  16
             TokenTypes.LITERAL_INT,
 77  16
             TokenTypes.LITERAL_LONG,
 78  16
             TokenTypes.LITERAL_FLOAT,
 79  16
             TokenTypes.LITERAL_DOUBLE,
 80  16
             TokenTypes.LITERAL_BOOLEAN,
 81  16
             TokenTypes.LITERAL_CHAR, })
 82  16
         .collect(Collectors.toSet()));
 83  
 
 84  
     /**
 85  
      * Option to ignore primitive types as params.
 86  
      */
 87  
     private boolean ignorePrimitiveTypes;
 88  
 
 89  
     /**
 90  
      * Sets ignoring primitive types as params.
 91  
      * @param ignorePrimitiveTypes true or false.
 92  
      */
 93  
     public void setIgnorePrimitiveTypes(boolean ignorePrimitiveTypes) {
 94  2
         this.ignorePrimitiveTypes = ignorePrimitiveTypes;
 95  2
     }
 96  
 
 97  
     @Override
 98  
     public int[] getDefaultTokens() {
 99  11
         return new int[] {
 100  
             TokenTypes.METHOD_DEF,
 101  
             TokenTypes.CTOR_DEF,
 102  
         };
 103  
     }
 104  
 
 105  
     @Override
 106  
     public int[] getAcceptableTokens() {
 107  10
         return new int[] {
 108  
             TokenTypes.METHOD_DEF,
 109  
             TokenTypes.CTOR_DEF,
 110  
             TokenTypes.LITERAL_CATCH,
 111  
             TokenTypes.FOR_EACH_CLAUSE,
 112  
         };
 113  
     }
 114  
 
 115  
     @Override
 116  
     public int[] getRequiredTokens() {
 117  22
         return CommonUtils.EMPTY_INT_ARRAY;
 118  
     }
 119  
 
 120  
     @Override
 121  
     public void visitToken(DetailAST ast) {
 122  
         // don't flag interfaces
 123  68
         final DetailAST container = ast.getParent().getParent();
 124  68
         if (container.getType() != TokenTypes.INTERFACE_DEF) {
 125  66
             if (ast.getType() == TokenTypes.LITERAL_CATCH) {
 126  4
                 visitCatch(ast);
 127  
             }
 128  62
             else if (ast.getType() == TokenTypes.FOR_EACH_CLAUSE) {
 129  4
                 visitForEachClause(ast);
 130  
             }
 131  
             else {
 132  58
                 visitMethod(ast);
 133  
             }
 134  
         }
 135  68
     }
 136  
 
 137  
     /**
 138  
      * Checks parameters of the method or ctor.
 139  
      * @param method method or ctor to check.
 140  
      */
 141  
     private void visitMethod(final DetailAST method) {
 142  58
         final DetailAST modifiers =
 143  58
             method.findFirstToken(TokenTypes.MODIFIERS);
 144  
         // exit on fast lane if there is nothing to check here
 145  
 
 146  58
         if (method.findFirstToken(TokenTypes.PARAMETERS)
 147  58
                 .findFirstToken(TokenTypes.PARAMETER_DEF) != null
 148  
                 // ignore abstract and native methods
 149  46
                 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null
 150  44
                 && modifiers.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
 151  
             // we can now be sure that there is at least one parameter
 152  42
             final DetailAST parameters =
 153  42
                 method.findFirstToken(TokenTypes.PARAMETERS);
 154  42
             DetailAST child = parameters.getFirstChild();
 155  108
             while (child != null) {
 156  
                 // children are PARAMETER_DEF and COMMA
 157  66
                 if (child.getType() == TokenTypes.PARAMETER_DEF) {
 158  54
                     checkParam(child);
 159  
                 }
 160  66
                 child = child.getNextSibling();
 161  
             }
 162  
         }
 163  58
     }
 164  
 
 165  
     /**
 166  
      * Checks parameter of the catch block.
 167  
      * @param catchClause catch block to check.
 168  
      */
 169  
     private void visitCatch(final DetailAST catchClause) {
 170  4
         checkParam(catchClause.findFirstToken(TokenTypes.PARAMETER_DEF));
 171  4
     }
 172  
 
 173  
     /**
 174  
      * Checks parameter of the for each clause.
 175  
      * @param forEachClause for each clause to check.
 176  
      */
 177  
     private void visitForEachClause(final DetailAST forEachClause) {
 178  4
         checkParam(forEachClause.findFirstToken(TokenTypes.VARIABLE_DEF));
 179  4
     }
 180  
 
 181  
     /**
 182  
      * Checks if the given parameter is final.
 183  
      * @param param parameter to check.
 184  
      */
 185  
     private void checkParam(final DetailAST param) {
 186  62
         if (param.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(TokenTypes.FINAL) == null
 187  43
                 && !isIgnoredParam(param)
 188  36
                 && !CheckUtils.isReceiverParameter(param)) {
 189  34
             final DetailAST paramName = param.findFirstToken(TokenTypes.IDENT);
 190  34
             final DetailAST firstNode = CheckUtils.getFirstNode(param);
 191  68
             log(firstNode.getLineNo(), firstNode.getColumnNo(),
 192  34
                 MSG_KEY, paramName.getText());
 193  
         }
 194  62
     }
 195  
 
 196  
     /**
 197  
      * Checks for skip current param due to <b>ignorePrimitiveTypes</b> option.
 198  
      * @param paramDef {@link TokenTypes#PARAMETER_DEF PARAMETER_DEF}
 199  
      * @return true if param has to be skipped.
 200  
      */
 201  
     private boolean isIgnoredParam(DetailAST paramDef) {
 202  43
         boolean result = false;
 203  43
         if (ignorePrimitiveTypes) {
 204  14
             final DetailAST parameterType = paramDef
 205  14
                 .findFirstToken(TokenTypes.TYPE).getFirstChild();
 206  14
             if (primitiveDataTypes.contains(parameterType.getType())) {
 207  7
                 result = true;
 208  
             }
 209  
         }
 210  43
         return result;
 211  
     }
 212  
 }