RightCurlyCheck.java

1
///////////////////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3
// Copyright (C) 2001-2022 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.blocks;
21
22
import java.util.Arrays;
23
import java.util.Locale;
24
25
import com.puppycrawl.tools.checkstyle.StatelessCheck;
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
import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30
import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
31
32
/**
33
 * <p>
34
 * Checks the placement of right curly braces ({@code '}'}) for code blocks. This check supports
35
 * if-else, try-catch-finally blocks, while-loops, for-loops,
36
 * method definitions, class definitions, constructor definitions,
37
 * instance, static initialization blocks, annotation definitions and enum definitions.
38
 * For right curly brace of expression blocks of arrays, lambdas and class instances
39
 * please follow issue
40
 * <a href="https://github.com/checkstyle/checkstyle/issues/5945">#5945</a>.
41
 * For right curly brace of enum constant please follow issue
42
 * <a href="https://github.com/checkstyle/checkstyle/issues/7519">#7519</a>.
43
 * </p>
44
 * <ul>
45
 * <li>
46
 * Property {@code option} - Specify the policy on placement of a right curly brace
47
 * (<code>'}'</code>).
48
 * Type is {@code com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyOption}.
49
 * Default value is {@code same}.
50
 * </li>
51
 * <li>
52
 * Property {@code tokens} - tokens to check
53
 * Type is {@code java.lang.String[]}.
54
 * Validation type is {@code tokenSet}.
55
 * Default value is:
56
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_TRY">
57
 * LITERAL_TRY</a>,
58
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_CATCH">
59
 * LITERAL_CATCH</a>,
60
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FINALLY">
61
 * LITERAL_FINALLY</a>,
62
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
63
 * LITERAL_IF</a>,
64
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
65
 * LITERAL_ELSE</a>.
66
 * </li>
67
 * </ul>
68
 * <p>
69
 * To configure the check:
70
 * </p>
71
 * <pre>
72
 * &lt;module name="RightCurly"/&gt;
73
 * </pre>
74
 * <p>
75
 * Example:
76
 * </p>
77
 * <pre>
78
 * public class Test {
79
 *
80
 *   public void test() {
81
 *
82
 *     if (foo) {
83
 *       bar();
84
 *     }           // violation, right curly must be in the same line as the 'else' keyword
85
 *     else {
86
 *       bar();
87
 *     }
88
 *
89
 *     if (foo) {
90
 *       bar();
91
 *     } else {     // OK
92
 *       bar();
93
 *     }
94
 *
95
 *     if (foo) { bar(); } int i = 0; // violation
96
 *                   // ^^^ statement is not allowed on same line after curly right brace
97
 *
98
 *     if (foo) { bar(); }            // OK
99
 *     int i = 0;
100
 *
101
 *     try {
102
 *       bar();
103
 *     }           // violation, rightCurly must be in the same line as 'catch' keyword
104
 *     catch (Exception e) {
105
 *       bar();
106
 *     }
107
 *
108
 *     try {
109
 *       bar();
110
 *     } catch (Exception e) { // OK
111
 *       bar();
112
 *     }
113
 *
114
 *   }                         // OK
115
 *
116
 *   public void testSingleLine() { bar(); } // OK, because singleline is allowed
117
 * }
118
 * </pre>
119
 * <p>
120
 * To configure the check with policy {@code alone} for {@code else} and
121
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
122
 * METHOD_DEF</a> tokens:
123
 * </p>
124
 * <pre>
125
 * &lt;module name=&quot;RightCurly&quot;&gt;
126
 *   &lt;property name=&quot;option&quot; value=&quot;alone&quot;/&gt;
127
 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_ELSE, METHOD_DEF&quot;/&gt;
128
 * &lt;/module&gt;
129
 * </pre>
130
 * <p>
131
 * Example:
132
 * </p>
133
 * <pre>
134
 * public class Test {
135
 *
136
 *   public void test() {
137
 *
138
 *     if (foo) {
139
 *       bar();
140
 *     } else { bar(); }   // violation, right curly must be alone on line
141
 *
142
 *     if (foo) {
143
 *       bar();
144
 *     } else {
145
 *       bar();
146
 *     }                   // OK
147
 *
148
 *     try {
149
 *       bar();
150
 *     } catch (Exception e) { // OK because config is set to token METHOD_DEF and LITERAL_ELSE
151
 *       bar();
152
 *     }
153
 *
154
 *   }                         // OK
155
 *
156
 *   public void violate() { bar; } // violation, singleline is not allowed here
157
 *
158
 *   public void ok() {
159
 *     bar();
160
 *   }                              // OK
161
 * }
162
 * </pre>
163
 * <p>
164
 * To configure the check with policy {@code alone_or_singleline} for {@code if} and
165
 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
166
 * METHOD_DEF</a>
167
 * tokens:
168
 * </p>
169
 * <pre>
170
 * &lt;module name=&quot;RightCurly&quot;&gt;
171
 *  &lt;property name=&quot;option&quot; value=&quot;alone_or_singleline&quot;/&gt;
172
 *  &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_IF, METHOD_DEF&quot;/&gt;
173
 * &lt;/module&gt;
174
 * </pre>
175
 * <p>
176
 * Example:
177
 * </p>
178
 * <pre>
179
 * public class Test {
180
 *
181
 *   public void test() {
182
 *
183
 *     if (foo) {
184
 *       bar();
185
 *     } else {        // violation, right curly must be alone on line
186
 *       bar();
187
 *     }
188
 *
189
 *     if (foo) {
190
 *       bar();
191
 *     }               // OK
192
 *     else {
193
 *       bar();
194
 *     }
195
 *
196
 *     try {
197
 *       bar();
198
 *     } catch (Exception e) {        // OK because config did not set token LITERAL_TRY
199
 *       bar();
200
 *     }
201
 *
202
 *   }                                // OK
203
 *
204
 *   public void violate() { bar(); } // OK , because singleline
205
 * }
206
 * </pre>
207
 * <p>
208
 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
209
 * </p>
210
 * <p>
211
 * Violation Message Keys:
212
 * </p>
213
 * <ul>
214
 * <li>
215
 * {@code line.alone}
216
 * </li>
217
 * <li>
218
 * {@code line.break.before}
219
 * </li>
220
 * <li>
221
 * {@code line.same}
222
 * </li>
223
 * </ul>
224
 *
225
 * @since 3.0
226
 */
227
@StatelessCheck
228
public class RightCurlyCheck extends AbstractCheck {
229
230
    /**
231
     * A key is pointing to the warning message text in "messages.properties"
232
     * file.
233
     */
234
    public static final String MSG_KEY_LINE_BREAK_BEFORE = "line.break.before";
235
236
    /**
237
     * A key is pointing to the warning message text in "messages.properties"
238
     * file.
239
     */
240
    public static final String MSG_KEY_LINE_ALONE = "line.alone";
241
242
    /**
243
     * A key is pointing to the warning message text in "messages.properties"
244
     * file.
245
     */
246
    public static final String MSG_KEY_LINE_SAME = "line.same";
247
248
    /**
249
     * Specify the policy on placement of a right curly brace (<code>'}'</code>).
250
     */
251
    private RightCurlyOption option = RightCurlyOption.SAME;
252
253
    /**
254
     * Setter to specify the policy on placement of a right curly brace (<code>'}'</code>).
255
     *
256
     * @param optionStr string to decode option from
257
     * @throws IllegalArgumentException if unable to decode
258
     */
259
    public void setOption(String optionStr) {
260
        option = RightCurlyOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
261
    }
262
263
    @Override
264
    public int[] getDefaultTokens() {
265
        return new int[] {
266
            TokenTypes.LITERAL_TRY,
267
            TokenTypes.LITERAL_CATCH,
268
            TokenTypes.LITERAL_FINALLY,
269
            TokenTypes.LITERAL_IF,
270
            TokenTypes.LITERAL_ELSE,
271
        };
272
    }
273
274
    @Override
275
    public int[] getAcceptableTokens() {
276
        return new int[] {
277
            TokenTypes.LITERAL_TRY,
278
            TokenTypes.LITERAL_CATCH,
279
            TokenTypes.LITERAL_FINALLY,
280
            TokenTypes.LITERAL_IF,
281
            TokenTypes.LITERAL_ELSE,
282
            TokenTypes.CLASS_DEF,
283
            TokenTypes.METHOD_DEF,
284
            TokenTypes.CTOR_DEF,
285
            TokenTypes.LITERAL_FOR,
286
            TokenTypes.LITERAL_WHILE,
287
            TokenTypes.LITERAL_DO,
288
            TokenTypes.STATIC_INIT,
289
            TokenTypes.INSTANCE_INIT,
290
            TokenTypes.ANNOTATION_DEF,
291
            TokenTypes.ENUM_DEF,
292
            TokenTypes.INTERFACE_DEF,
293
            TokenTypes.RECORD_DEF,
294
            TokenTypes.COMPACT_CTOR_DEF,
295
        };
296
    }
297
298
    @Override
299
    public int[] getRequiredTokens() {
300
        return CommonUtil.EMPTY_INT_ARRAY;
301
    }
302
303
    @Override
304
    public void visitToken(DetailAST ast) {
305
        final Details details = Details.getDetails(ast);
306
        final DetailAST rcurly = details.rcurly;
307
308
        if (rcurly != null) {
309
            final String violation = validate(details);
310
            if (!violation.isEmpty()) {
311 1 1. visitToken : Replaced integer addition with subtraction → KILLED
                log(rcurly, violation, "}", rcurly.getColumnNo() + 1);
312
            }
313
        }
314
    }
315
316
    /**
317
     * Does general validation.
318
     *
319
     * @param details for validation.
320
     * @return violation message or empty string
321
     *     if there was no violation during validation.
322
     */
323
    private String validate(Details details) {
324
        String violation = "";
325
        if (shouldHaveLineBreakBefore(option, details)) {
326
            violation = MSG_KEY_LINE_BREAK_BEFORE;
327
        }
328
        else if (shouldBeOnSameLine(option, details)) {
329
            violation = MSG_KEY_LINE_SAME;
330
        }
331 1 1. validate : Replaced integer subtraction with addition → KILLED
        else if (shouldBeAloneOnLine(option, details, getLine(details.rcurly.getLineNo() - 1))) {
332
            violation = MSG_KEY_LINE_ALONE;
333
        }
334
        return violation;
335
    }
336
337
    /**
338
     * Checks whether a right curly should have a line break before.
339
     *
340
     * @param bracePolicy option for placing the right curly brace.
341
     * @param details details for validation.
342
     * @return true if a right curly should have a line break before.
343
     */
344
    private static boolean shouldHaveLineBreakBefore(RightCurlyOption bracePolicy,
345
                                                     Details details) {
346
        return bracePolicy == RightCurlyOption.SAME
347
                && !hasLineBreakBefore(details.rcurly)
348
                && !TokenUtil.areOnSameLine(details.lcurly, details.rcurly);
349
    }
350
351
    /**
352
     * Checks that a right curly should be on the same line as the next statement.
353
     *
354
     * @param bracePolicy option for placing the right curly brace
355
     * @param details Details for validation
356
     * @return true if a right curly should be alone on a line.
357
     */
358
    private static boolean shouldBeOnSameLine(RightCurlyOption bracePolicy, Details details) {
359
        return bracePolicy == RightCurlyOption.SAME
360
                && !details.shouldCheckLastRcurly
361
                && !TokenUtil.areOnSameLine(details.rcurly, details.nextToken);
362
    }
363
364
    /**
365
     * Checks that a right curly should be alone on a line.
366
     *
367
     * @param bracePolicy option for placing the right curly brace
368
     * @param details Details for validation
369
     * @param targetSrcLine A string with contents of rcurly's line
370
     * @return true if a right curly should be alone on a line.
371
     */
372
    private static boolean shouldBeAloneOnLine(RightCurlyOption bracePolicy,
373
                                               Details details,
374
                                               String targetSrcLine) {
375
        return bracePolicy == RightCurlyOption.ALONE
376
                    && shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
377
                || (bracePolicy == RightCurlyOption.ALONE_OR_SINGLELINE
378
                    || details.shouldCheckLastRcurly)
379
                    && shouldBeAloneOnLineWithNotAloneOption(details, targetSrcLine);
380
    }
381
382
    /**
383
     * Whether right curly should be alone on line when ALONE option is used.
384
     *
385
     * @param details details for validation.
386
     * @param targetSrcLine A string with contents of rcurly's line
387
     * @return true, if right curly should be alone on line when ALONE option is used.
388
     */
389
    private static boolean shouldBeAloneOnLineWithAloneOption(Details details,
390
                                                              String targetSrcLine) {
391
        return !isAloneOnLine(details, targetSrcLine);
392
    }
393
394
    /**
395
     * Whether right curly should be alone on line when ALONE_OR_SINGLELINE or SAME option is used.
396
     *
397
     * @param details details for validation.
398
     * @param targetSrcLine A string with contents of rcurly's line
399
     * @return true, if right curly should be alone on line
400
     *         when ALONE_OR_SINGLELINE or SAME option is used.
401
     */
402
    private static boolean shouldBeAloneOnLineWithNotAloneOption(Details details,
403
                                                                 String targetSrcLine) {
404
        return shouldBeAloneOnLineWithAloneOption(details, targetSrcLine)
405
                && !isBlockAloneOnSingleLine(details);
406
    }
407
408
    /**
409
     * Checks whether right curly is alone on a line.
410
     *
411
     * @param details for validation.
412
     * @param targetSrcLine A string with contents of rcurly's line
413
     * @return true if right curly is alone on a line.
414
     */
415
    private static boolean isAloneOnLine(Details details, String targetSrcLine) {
416
        final DetailAST rcurly = details.rcurly;
417
        final DetailAST nextToken = details.nextToken;
418
        return (nextToken == null || !TokenUtil.areOnSameLine(rcurly, nextToken)
419
            || skipDoubleBraceInstInit(details))
420
            && CommonUtil.hasWhitespaceBefore(details.rcurly.getColumnNo(),
421
               targetSrcLine);
422
    }
423
424
    /**
425
     * This method determines if the double brace initialization should be skipped over by the
426
     * check. Double brace initializations are treated differently. The corresponding inner
427
     * rcurly is treated as if it was alone on line even when it may be followed by another
428
     * rcurly and a semi, raising no violations.
429
     * <i>Please do note though that the line should not contain anything other than the following
430
     * right curly and the semi following it or else violations will be raised.</i>
431
     * Only the kind of double brace initializations shown in the following example code will be
432
     * skipped over:<br>
433
     * <pre>
434
     *     {@code Map<String, String> map = new LinkedHashMap<>() {{
435
     *           put("alpha", "man");
436
     *       }}; // no violation}
437
     * </pre>
438
     *
439
     * @param details {@link Details} object containing the details relevant to the rcurly
440
     * @return if the double brace initialization rcurly should be skipped over by the check
441
     */
442
    private static boolean skipDoubleBraceInstInit(Details details) {
443
        boolean skipDoubleBraceInstInit = false;
444
        final DetailAST tokenAfterNextToken = Details.getNextToken(details.nextToken);
445
        if (tokenAfterNextToken != null) {
446
            final DetailAST rcurly = details.rcurly;
447
            skipDoubleBraceInstInit = rcurly.getParent().getParent()
448
                    .getType() == TokenTypes.INSTANCE_INIT
449
                    && details.nextToken.getType() == TokenTypes.RCURLY
450
                    && !TokenUtil.areOnSameLine(rcurly, Details.getNextToken(tokenAfterNextToken));
451
        }
452
        return skipDoubleBraceInstInit;
453
    }
454
455
    /**
456
     * Checks whether block has a single-line format and is alone on a line.
457
     *
458
     * @param details for validation.
459
     * @return true if block has single-line format and is alone on a line.
460
     */
461
    private static boolean isBlockAloneOnSingleLine(Details details) {
462
        DetailAST nextToken = details.nextToken;
463
464
        while (nextToken != null && nextToken.getType() == TokenTypes.LITERAL_ELSE) {
465
            nextToken = Details.getNextToken(nextToken);
466
        }
467
468
        if (nextToken != null && nextToken.getType() == TokenTypes.DO_WHILE) {
469
            final DetailAST doWhileSemi = nextToken.getParent().getLastChild();
470
            nextToken = Details.getNextToken(doWhileSemi);
471
        }
472
473
        return TokenUtil.areOnSameLine(details.lcurly, details.rcurly)
474
            && (nextToken == null || !TokenUtil.areOnSameLine(details.rcurly, nextToken)
475
                || isRightcurlyFollowedBySemicolon(details));
476
    }
477
478
    /**
479
     * Checks whether the right curly is followed by a semicolon.
480
     *
481
     * @param details details for validation.
482
     * @return true if the right curly is followed by a semicolon.
483
     */
484
    private static boolean isRightcurlyFollowedBySemicolon(Details details) {
485
        return details.nextToken.getType() == TokenTypes.SEMI;
486
    }
487
488
    /**
489
     * Checks if right curly has line break before.
490
     *
491
     * @param rightCurly right curly token.
492
     * @return true, if right curly has line break before.
493
     */
494
    private static boolean hasLineBreakBefore(DetailAST rightCurly) {
495
        DetailAST previousToken = rightCurly.getPreviousSibling();
496
        if (previousToken == null) {
497
            previousToken = rightCurly.getParent();
498
        }
499
        return !TokenUtil.areOnSameLine(rightCurly, previousToken);
500
    }
501
502
    /**
503
     * Structure that contains all details for validation.
504
     */
505
    private static final class Details {
506
507
        /**
508
         * Token types that identify tokens that will never have SLIST in their AST.
509
         */
510
        private static final int[] TOKENS_WITH_NO_CHILD_SLIST = {
511
            TokenTypes.CLASS_DEF,
512
            TokenTypes.ENUM_DEF,
513
            TokenTypes.ANNOTATION_DEF,
514
            TokenTypes.INTERFACE_DEF,
515
            TokenTypes.RECORD_DEF,
516
        };
517
518
        /** Right curly. */
519
        private final DetailAST rcurly;
520
        /** Left curly. */
521
        private final DetailAST lcurly;
522
        /** Next token. */
523
        private final DetailAST nextToken;
524
        /** Should check last right curly. */
525
        private final boolean shouldCheckLastRcurly;
526
527
        /**
528
         * Constructor.
529
         *
530
         * @param lcurly the lcurly of the token whose details are being collected
531
         * @param rcurly the rcurly of the token whose details are being collected
532
         * @param nextToken the token after the token whose details are being collected
533
         * @param shouldCheckLastRcurly boolean value to determine if to check last rcurly
534
         */
535
        private Details(DetailAST lcurly, DetailAST rcurly,
536
                        DetailAST nextToken, boolean shouldCheckLastRcurly) {
537
            this.lcurly = lcurly;
538
            this.rcurly = rcurly;
539
            this.nextToken = nextToken;
540
            this.shouldCheckLastRcurly = shouldCheckLastRcurly;
541
        }
542
543
        /**
544
         * Collects validation Details.
545
         *
546
         * @param ast a {@code DetailAST} value
547
         * @return object containing all details to make a validation
548
         */
549
        private static Details getDetails(DetailAST ast) {
550
            final Details details;
551
            switch (ast.getType()) {
552
                case TokenTypes.LITERAL_TRY:
553
                case TokenTypes.LITERAL_CATCH:
554
                case TokenTypes.LITERAL_FINALLY:
555
                    details = getDetailsForTryCatchFinally(ast);
556
                    break;
557
                case TokenTypes.LITERAL_IF:
558
                case TokenTypes.LITERAL_ELSE:
559
                    details = getDetailsForIfElse(ast);
560
                    break;
561
                case TokenTypes.LITERAL_DO:
562
                case TokenTypes.LITERAL_WHILE:
563
                case TokenTypes.LITERAL_FOR:
564
                    details = getDetailsForLoops(ast);
565
                    break;
566
                default:
567
                    details = getDetailsForOthers(ast);
568
                    break;
569
            }
570
            return details;
571
        }
572
573
        /**
574
         * Collects validation details for LITERAL_TRY, LITERAL_CATCH, and LITERAL_FINALLY.
575
         *
576
         * @param ast a {@code DetailAST} value
577
         * @return object containing all details to make a validation
578
         */
579
        private static Details getDetailsForTryCatchFinally(DetailAST ast) {
580
            final DetailAST lcurly;
581
            DetailAST nextToken;
582
            final int tokenType = ast.getType();
583
            if (tokenType == TokenTypes.LITERAL_TRY) {
584
                if (ast.getFirstChild().getType() == TokenTypes.RESOURCE_SPECIFICATION) {
585
                    lcurly = ast.getFirstChild().getNextSibling();
586
                }
587
                else {
588
                    lcurly = ast.getFirstChild();
589
                }
590
                nextToken = lcurly.getNextSibling();
591
            }
592
            else {
593
                nextToken = ast.getNextSibling();
594
                lcurly = ast.getLastChild();
595
            }
596
597
            final boolean shouldCheckLastRcurly;
598
            if (nextToken == null) {
599
                shouldCheckLastRcurly = true;
600
                nextToken = getNextToken(ast);
601
            }
602
            else {
603
                shouldCheckLastRcurly = false;
604
            }
605
606
            final DetailAST rcurly = lcurly.getLastChild();
607
            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
608
        }
609
610
        /**
611
         * Collects validation details for LITERAL_IF and LITERAL_ELSE.
612
         *
613
         * @param ast a {@code DetailAST} value
614
         * @return object containing all details to make a validation
615
         */
616
        private static Details getDetailsForIfElse(DetailAST ast) {
617
            final boolean shouldCheckLastRcurly;
618
            final DetailAST lcurly;
619
            DetailAST nextToken = ast.findFirstToken(TokenTypes.LITERAL_ELSE);
620
621
            if (nextToken == null) {
622
                shouldCheckLastRcurly = true;
623
                nextToken = getNextToken(ast);
624
                lcurly = ast.getLastChild();
625
            }
626
            else {
627
                shouldCheckLastRcurly = false;
628
                lcurly = nextToken.getPreviousSibling();
629
            }
630
631
            DetailAST rcurly = null;
632
            if (lcurly.getType() == TokenTypes.SLIST) {
633
                rcurly = lcurly.getLastChild();
634
            }
635
            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
636
        }
637
638
        /**
639
         * Collects validation details for CLASS_DEF, RECORD_DEF, METHOD DEF, CTOR_DEF, STATIC_INIT,
640
         * INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, and COMPACT_CTOR_DEF.
641
         *
642
         * @param ast a {@code DetailAST} value
643
         * @return an object containing all details to make a validation
644
         */
645
        private static Details getDetailsForOthers(DetailAST ast) {
646
            DetailAST rcurly = null;
647
            final DetailAST lcurly;
648
            final int tokenType = ast.getType();
649
            if (isTokenWithNoChildSlist(tokenType)) {
650
                final DetailAST child = ast.getLastChild();
651
                lcurly = child.getFirstChild();
652
                rcurly = child.getLastChild();
653
            }
654
            else {
655
                lcurly = ast.findFirstToken(TokenTypes.SLIST);
656
                if (lcurly != null) {
657
                    // SLIST could be absent if method is abstract
658
                    rcurly = lcurly.getLastChild();
659
                }
660
            }
661
            return new Details(lcurly, rcurly, getNextToken(ast), true);
662
        }
663
664
        /**
665
         * Tests whether the provided tokenType will never have a SLIST as child in its AST.
666
         * Like CLASS_DEF, ANNOTATION_DEF etc.
667
         *
668
         * @param tokenType the tokenType to test against.
669
         * @return weather provided tokenType is definition token.
670
         */
671
        private static boolean isTokenWithNoChildSlist(int tokenType) {
672
            return Arrays.stream(TOKENS_WITH_NO_CHILD_SLIST).anyMatch(token -> token == tokenType);
673
        }
674
675
        /**
676
         * Collects validation details for loops' tokens.
677
         *
678
         * @param ast a {@code DetailAST} value
679
         * @return an object containing all details to make a validation
680
         */
681
        private static Details getDetailsForLoops(DetailAST ast) {
682
            DetailAST rcurly = null;
683
            final DetailAST lcurly;
684
            final DetailAST nextToken;
685
            final int tokenType = ast.getType();
686
            final boolean shouldCheckLastRcurly;
687
            if (tokenType == TokenTypes.LITERAL_DO) {
688
                shouldCheckLastRcurly = false;
689
                nextToken = ast.findFirstToken(TokenTypes.DO_WHILE);
690
                lcurly = ast.findFirstToken(TokenTypes.SLIST);
691
                if (lcurly != null) {
692
                    rcurly = lcurly.getLastChild();
693
                }
694
            }
695
            else {
696
                shouldCheckLastRcurly = true;
697
                lcurly = ast.findFirstToken(TokenTypes.SLIST);
698
                if (lcurly != null) {
699
                    // SLIST could be absent in code like "while(true);"
700
                    rcurly = lcurly.getLastChild();
701
                }
702
                nextToken = getNextToken(ast);
703
            }
704
            return new Details(lcurly, rcurly, nextToken, shouldCheckLastRcurly);
705
        }
706
707
        /**
708
         * Finds next token after the given one.
709
         *
710
         * @param ast the given node.
711
         * @return the token which represents next lexical item.
712
         */
713
        private static DetailAST getNextToken(DetailAST ast) {
714
            DetailAST next = null;
715
            DetailAST parent = ast;
716
            while (next == null && parent != null) {
717
                next = parent.getNextSibling();
718
                parent = parent.getParent();
719
            }
720
            return next;
721
        }
722
    }
723
}

Mutations

311

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheckTest.[engine:junit-jupiter]/[class:com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheckTest]/[method:testRightCurlySameAndLiteralDo()]
Replaced integer addition with subtraction → KILLED

331

1.1
Location : validate
Killed by : com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheckTest.[engine:junit-jupiter]/[class:com.puppycrawl.tools.checkstyle.checks.blocks.RightCurlyCheckTest]/[method:testNewTokensAlone()]
Replaced integer subtraction with addition → KILLED

Active mutators

Tests examined


Report generated by PIT 1.8.0