| 1 | //////////////////////////////////////////////////////////////////////////////// | |
| 2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
| 3 | // Copyright (C) 2001-2018 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.ArrayDeque; | |
| 23 | import java.util.Deque; | |
| 24 | ||
| 25 | import com.puppycrawl.tools.checkstyle.FileStatefulCheck; | |
| 26 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
| 27 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
| 28 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
| 29 | ||
| 30 | /** | |
| 31 |  * Restricts the number of statements per line to one. | |
| 32 |  * <p> | |
| 33 |  *     Rationale: It's very difficult to read multiple statements on one line. | |
| 34 |  * </p> | |
| 35 |  * <p> | |
| 36 |  *     In the Java programming language, statements are the fundamental unit of | |
| 37 |  *     execution. All statements except blocks are terminated by a semicolon. | |
| 38 |  *     Blocks are denoted by open and close curly braces. | |
| 39 |  * </p> | |
| 40 |  * <p> | |
| 41 |  *     OneStatementPerLineCheck checks the following types of statements: | |
| 42 |  *     variable declaration statements, empty statements, assignment statements, | |
| 43 |  *     expression statements, increment statements, object creation statements, | |
| 44 |  *     'for loop' statements, 'break' statements, 'continue' statements, | |
| 45 |  *     'return' statements, import statements. | |
| 46 |  * </p> | |
| 47 |  * <p> | |
| 48 |  *     The following examples will be flagged as a violation: | |
| 49 |  * </p> | |
| 50 |  * <pre> | |
| 51 |  *     //Each line causes violation: | |
| 52 |  *     int var1; int var2; | |
| 53 |  *     var1 = 1; var2 = 2; | |
| 54 |  *     int var1 = 1; int var2 = 2; | |
| 55 |  *     var1++; var2++; | |
| 56 |  *     Object obj1 = new Object(); Object obj2 = new Object(); | |
| 57 |  *     import java.io.EOFException; import java.io.BufferedReader; | |
| 58 |  *     ;; //two empty statements on the same line. | |
| 59 |  * | |
| 60 |  *     //Multi-line statements: | |
| 61 |  *     int var1 = 1 | |
| 62 |  *     ; var2 = 2; //violation here | |
| 63 |  *     int o = 1, p = 2, | |
| 64 |  *     r = 5; int t; //violation here | |
| 65 |  * </pre> | |
| 66 |  * | |
| 67 |  * @author Alexander Jesse | |
| 68 |  * @author Oliver Burn | |
| 69 |  * @author Andrei Selkin | |
| 70 |  */ | |
| 71 | @FileStatefulCheck | |
| 72 | public final class OneStatementPerLineCheck extends AbstractCheck { | |
| 73 | ||
| 74 |     /** | |
| 75 |      * A key is pointing to the warning message text in "messages.properties" | |
| 76 |      * file. | |
| 77 |      */ | |
| 78 |     public static final String MSG_KEY = "multiple.statements.line"; | |
| 79 | ||
| 80 |     /** | |
| 81 |      * Counts number of semicolons in nested lambdas. | |
| 82 |      */ | |
| 83 |     private final Deque<Integer> countOfSemiInLambda = new ArrayDeque<>(); | |
| 84 | ||
| 85 |     /** | |
| 86 |      * Hold the line-number where the last statement ended. | |
| 87 |      */ | |
| 88 |     private int lastStatementEnd = -1; | |
| 89 | ||
| 90 |     /** | |
| 91 |      * Hold the line-number where the last 'for-loop' statement ended. | |
| 92 |      */ | |
| 93 |     private int forStatementEnd = -1; | |
| 94 | ||
| 95 |     /** | |
| 96 |      * The for-header usually has 3 statements on one line, but THIS IS OK. | |
| 97 |      */ | |
| 98 |     private boolean inForHeader; | |
| 99 | ||
| 100 |     /** | |
| 101 |      * Holds if current token is inside lambda. | |
| 102 |      */ | |
| 103 |     private boolean isInLambda; | |
| 104 | ||
| 105 |     /** | |
| 106 |      * Hold the line-number where the last lambda statement ended. | |
| 107 |      */ | |
| 108 |     private int lambdaStatementEnd = -1; | |
| 109 | ||
| 110 |     @Override | |
| 111 |     public int[] getDefaultTokens() { | |
| 112 | 
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED | 
        return getRequiredTokens(); | 
| 113 |     } | |
| 114 | ||
| 115 |     @Override | |
| 116 |     public int[] getAcceptableTokens() { | |
| 117 | 
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED | 
        return getRequiredTokens(); | 
| 118 |     } | |
| 119 | ||
| 120 |     @Override | |
| 121 |     public int[] getRequiredTokens() { | |
| 122 | 
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED | 
        return new int[] { | 
| 123 |             TokenTypes.SEMI, | |
| 124 |             TokenTypes.FOR_INIT, | |
| 125 |             TokenTypes.FOR_ITERATOR, | |
| 126 |             TokenTypes.LAMBDA, | |
| 127 |         }; | |
| 128 |     } | |
| 129 | ||
| 130 |     @Override | |
| 131 |     public void beginTree(DetailAST rootAST) { | |
| 132 |         inForHeader = false; | |
| 133 |         lastStatementEnd = -1; | |
| 134 |         forStatementEnd = -1; | |
| 135 |         isInLambda = false; | |
| 136 |     } | |
| 137 | ||
| 138 |     @Override | |
| 139 |     public void visitToken(DetailAST ast) { | |
| 140 |         switch (ast.getType()) { | |
| 141 |             case TokenTypes.SEMI: | |
| 142 | 
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::checkIfSemicolonIsInDifferentLineThanPrevious → KILLED | 
                checkIfSemicolonIsInDifferentLineThanPrevious(ast); | 
| 143 |                 break; | |
| 144 |             case TokenTypes.FOR_ITERATOR: | |
| 145 |                 forStatementEnd = ast.getLineNo(); | |
| 146 |                 break; | |
| 147 |             case TokenTypes.LAMBDA: | |
| 148 |                 isInLambda = true; | |
| 149 | 
1
1. visitToken : removed call to java/util/Deque::push → KILLED | 
                countOfSemiInLambda.push(0); | 
| 150 |                 break; | |
| 151 |             default: | |
| 152 |                 inForHeader = true; | |
| 153 |                 break; | |
| 154 |         } | |
| 155 |     } | |
| 156 | ||
| 157 |     @Override | |
| 158 |     public void leaveToken(DetailAST ast) { | |
| 159 |         switch (ast.getType()) { | |
| 160 |             case TokenTypes.SEMI: | |
| 161 |                 lastStatementEnd = ast.getLineNo(); | |
| 162 |                 forStatementEnd = -1; | |
| 163 |                 lambdaStatementEnd = -1; | |
| 164 |                 break; | |
| 165 |             case TokenTypes.FOR_ITERATOR: | |
| 166 |                 inForHeader = false; | |
| 167 |                 break; | |
| 168 |             case TokenTypes.LAMBDA: | |
| 169 |                 countOfSemiInLambda.pop(); | |
| 170 | 
1
1. leaveToken : negated conditional → KILLED | 
                if (countOfSemiInLambda.isEmpty()) { | 
| 171 |                     isInLambda = false; | |
| 172 |                 } | |
| 173 |                 lambdaStatementEnd = ast.getLineNo(); | |
| 174 |                 break; | |
| 175 |             default: | |
| 176 |                 break; | |
| 177 |         } | |
| 178 |     } | |
| 179 | ||
| 180 |     /** | |
| 181 |      * Checks if given semicolon is in different line than previous. | |
| 182 |      * @param ast semicolon to check | |
| 183 |      */ | |
| 184 |     private void checkIfSemicolonIsInDifferentLineThanPrevious(DetailAST ast) { | |
| 185 |         DetailAST currentStatement = ast; | |
| 186 |         final boolean hasResourcesPrevSibling = | |
| 187 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED | 
                currentStatement.getPreviousSibling() != null | 
| 188 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED | 
                        && currentStatement.getPreviousSibling().getType() == TokenTypes.RESOURCES; | 
| 189 | 
2
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED 2. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED  | 
        if (!hasResourcesPrevSibling && isMultilineStatement(currentStatement)) { | 
| 190 |             currentStatement = ast.getPreviousSibling(); | |
| 191 |         } | |
| 192 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED | 
        if (isInLambda) { | 
| 193 |             int countOfSemiInCurrentLambda = countOfSemiInLambda.pop(); | |
| 194 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : Changed increment from 1 to -1 → KILLED | 
            countOfSemiInCurrentLambda++; | 
| 195 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : removed call to java/util/Deque::push → KILLED | 
            countOfSemiInLambda.push(countOfSemiInCurrentLambda); | 
| 196 | 
3
1. checkIfSemicolonIsInDifferentLineThanPrevious : changed conditional boundary → KILLED 2. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED 3. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED  | 
            if (!inForHeader && countOfSemiInCurrentLambda > 1 | 
| 197 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED | 
                    && isOnTheSameLine(currentStatement, | 
| 198 |                     lastStatementEnd, forStatementEnd, | |
| 199 |                     lambdaStatementEnd)) { | |
| 200 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::log → KILLED | 
                log(ast, MSG_KEY); | 
| 201 |             } | |
| 202 |         } | |
| 203 | 
2
1. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED 2. checkIfSemicolonIsInDifferentLineThanPrevious : negated conditional → KILLED  | 
        else if (!inForHeader && isOnTheSameLine(currentStatement, lastStatementEnd, | 
| 204 |                 forStatementEnd, lambdaStatementEnd)) { | |
| 205 | 
1
1. checkIfSemicolonIsInDifferentLineThanPrevious : removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::log → KILLED | 
            log(ast, MSG_KEY); | 
| 206 |         } | |
| 207 |     } | |
| 208 | ||
| 209 |     /** | |
| 210 |      * Checks whether two statements are on the same line. | |
| 211 |      * @param ast token for the current statement. | |
| 212 |      * @param lastStatementEnd the line-number where the last statement ended. | |
| 213 |      * @param forStatementEnd the line-number where the last 'for-loop' | |
| 214 |      *                        statement ended. | |
| 215 |      * @param lambdaStatementEnd the line-number where the last lambda | |
| 216 |      *                        statement ended. | |
| 217 |      * @return true if two statements are on the same line. | |
| 218 |      */ | |
| 219 |     private static boolean isOnTheSameLine(DetailAST ast, int lastStatementEnd, | |
| 220 |                                            int forStatementEnd, int lambdaStatementEnd) { | |
| 221 | 
3
1. isOnTheSameLine : negated conditional → KILLED 2. isOnTheSameLine : negated conditional → KILLED 3. isOnTheSameLine : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED  | 
        return lastStatementEnd == ast.getLineNo() && forStatementEnd != ast.getLineNo() | 
| 222 | 
1
1. isOnTheSameLine : negated conditional → KILLED | 
                && lambdaStatementEnd != ast.getLineNo(); | 
| 223 |     } | |
| 224 | ||
| 225 |     /** | |
| 226 |      * Checks whether statement is multiline. | |
| 227 |      * @param ast token for the current statement. | |
| 228 |      * @return true if one statement is distributed over two or more lines. | |
| 229 |      */ | |
| 230 |     private static boolean isMultilineStatement(DetailAST ast) { | |
| 231 |         final boolean multiline; | |
| 232 | 
1
1. isMultilineStatement : negated conditional → KILLED | 
        if (ast.getPreviousSibling() == null) { | 
| 233 |             multiline = false; | |
| 234 |         } | |
| 235 |         else { | |
| 236 |             final DetailAST prevSibling = ast.getPreviousSibling(); | |
| 237 | 
1
1. isMultilineStatement : negated conditional → KILLED | 
            multiline = prevSibling.getLineNo() != ast.getLineNo() | 
| 238 | 
1
1. isMultilineStatement : negated conditional → KILLED | 
                    && ast.getParent() != null; | 
| 239 |         } | |
| 240 | 
1
1. isMultilineStatement : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED | 
        return multiline; | 
| 241 |     } | |
| 242 | ||
| 243 | } | |
Mutations | ||
| 112 | 
 
 1.1  | 
|
| 117 | 
 
 1.1  | 
|
| 122 | 
 
 1.1  | 
|
| 142 | 
 
 1.1  | 
|
| 149 | 
 
 1.1  | 
|
| 170 | 
 
 1.1  | 
|
| 187 | 
 
 1.1  | 
|
| 188 | 
 
 1.1  | 
|
| 189 | 
 
 1.1 2.2  | 
|
| 192 | 
 
 1.1  | 
|
| 194 | 
 
 1.1  | 
|
| 195 | 
 
 1.1  | 
|
| 196 | 
 
 1.1 2.2 3.3  | 
|
| 197 | 
 
 1.1  | 
|
| 200 | 
 
 1.1  | 
|
| 203 | 
 
 1.1 2.2  | 
|
| 205 | 
 
 1.1  | 
|
| 221 | 
 
 1.1 2.2 3.3  | 
|
| 222 | 
 
 1.1  | 
|
| 232 | 
 
 1.1  | 
|
| 237 | 
 
 1.1  | 
|
| 238 | 
 
 1.1  | 
|
| 240 | 
 
 1.1  |