OneStatementPerLineCheck.java

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
Location : getDefaultTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

117

1.1
Location : getAcceptableTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

122

1.1
Location : getRequiredTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

142

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::checkIfSemicolonIsInDifferentLineThanPrevious → KILLED

149

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
removed call to java/util/Deque::push → KILLED

170

1.1
Location : leaveToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

187

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

188

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testWithMultilineStatements(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

189

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testWithMultilineStatements(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

2.2
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

192

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

194

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.oneStatementNonCompilableInputTest(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
Changed increment from 1 to -1 → KILLED

195

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
removed call to java/util/Deque::push → KILLED

196

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
changed conditional boundary → KILLED

2.2
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.oneStatementNonCompilableInputTest(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

3.3
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

197

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.oneStatementNonCompilableInputTest(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

200

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.oneStatementNonCompilableInputTest(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::log → KILLED

203

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

2.2
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

205

1.1
Location : checkIfSemicolonIsInDifferentLineThanPrevious
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/OneStatementPerLineCheck::log → KILLED

221

1.1
Location : isOnTheSameLine
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

2.2
Location : isOnTheSameLine
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

3.3
Location : isOnTheSameLine
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

222

1.1
Location : isOnTheSameLine
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

232

1.1
Location : isMultilineStatement
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

237

1.1
Location : isMultilineStatement
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testWithMultilineStatements(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

238

1.1
Location : isMultilineStatement
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testWithMultilineStatements(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
negated conditional → KILLED

240

1.1
Location : isMultilineStatement
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest.testMultiCaseClass(com.puppycrawl.tools.checkstyle.checks.coding.OneStatementPerLineCheckTest)
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

Active mutators

Tests examined


Report generated by PIT 1.3.1