001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.blocks;
021
022import java.util.Optional;
023
024import com.puppycrawl.tools.checkstyle.StatelessCheck;
025import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
026import com.puppycrawl.tools.checkstyle.api.DetailAST;
027import com.puppycrawl.tools.checkstyle.api.TokenTypes;
028import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
029import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
030
031/**
032 * <p>
033 * Checks for braces around code blocks.
034 * </p>
035 * <ul>
036 * <li>
037 * Property {@code allowSingleLineStatement} - allow single-line statements without braces.
038 * Type is {@code boolean}.
039 * Default value is {@code false}.
040 * </li>
041 * <li>
042 * Property {@code allowEmptyLoopBody} - allow loops with empty bodies.
043 * Type is {@code boolean}.
044 * Default value is {@code false}.
045 * </li>
046 * <li>
047 * Property {@code tokens} - tokens to check
048 * Type is {@code java.lang.String[]}.
049 * Validation type is {@code tokenSet}.
050 * Default value is:
051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_DO">
052 * LITERAL_DO</a>,
053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_ELSE">
054 * LITERAL_ELSE</a>,
055 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_FOR">
056 * LITERAL_FOR</a>,
057 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_IF">
058 * LITERAL_IF</a>,
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LITERAL_WHILE">
060 * LITERAL_WHILE</a>.
061 * </li>
062 * </ul>
063 * <p>
064 * To configure the check:
065 * </p>
066 * <pre>
067 * &lt;module name="NeedBraces"/&gt;
068 * </pre>
069 * <p>Example:</p>
070 * <pre>
071 * if (obj.isValid()) return true; // violation, single-line statements not allowed without braces
072 * if (true) {                     // OK
073 *     return true;
074 * } else                          // violation, single-line statements not allowed without braces
075 *     return false;
076 * for (int i = 0; i &lt; 5; i++) {   // OK
077 *      ++count;
078 * }
079 * do                              // violation, single-line statements not allowed without braces
080 *     ++count;
081 * while (false);
082 * for (int j = 0; j &lt; 10; j++); // violation, empty loop body not allowed
083 * for(int i = 0; i &lt; 10; value.incrementValue()); // violation, empty loop body not allowed
084 * while (counter &lt; 10)          // violation, single-line statements not allowed without braces
085 *     ++count;
086 * while (value.incrementValue() &lt; 5); // violation, empty loop body not allowed
087 * switch (num) {
088 *   case 1: counter++; break;         // OK
089 * }
090 * </pre>
091 * <p>
092 * To configure the check for {@code if} and {@code else} blocks:
093 * </p>
094 * <pre>
095 * &lt;module name=&quot;NeedBraces&quot;&gt;
096 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_IF, LITERAL_ELSE&quot;/&gt;
097 * &lt;/module&gt;
098 * </pre>
099 * <p>Example:</p>
100 * <pre>
101 * if (obj.isValid()) return true; // violation, single-line statements not allowed without braces
102 * if (true) {                     // OK
103 *     return true;
104 * } else                          // violation, single-line statements not allowed without braces
105 *     return false;
106 * for (int i = 0; i &lt; 5; i++) {   // OK
107 *      ++count;
108 * }
109 * do                              // OK
110 *     ++count;
111 * while (false);
112 * for (int j = 0; j &lt; 10; j++);   // OK
113 * for(int i = 0; i &lt; 10; value.incrementValue()); // OK
114 * while (counter &lt; 10)                            // OK
115 *     ++count;
116 * while (value.incrementValue() &lt; 5); // OK
117 * switch (num) {
118 *   case 1: counter++; break;         // OK
119 * }
120 * </pre>
121 * <p>
122 * To configure the check to allow single-line statements
123 * ({@code if, while, do-while, for}) without braces:
124 * </p>
125 * <pre>
126 * &lt;module name=&quot;NeedBraces&quot;&gt;
127 *   &lt;property name=&quot;allowSingleLineStatement&quot; value=&quot;true&quot;/&gt;
128 *   &lt;property name=&quot;tokens&quot;
129 *          value=&quot;LITERAL_IF, LITERAL_WHILE, LITERAL_DO, LITERAL_FOR&quot;/&gt;
130 * &lt;/module&gt;
131 * </pre>
132 * <p>
133 * Example:
134 * </p>
135 * <pre>
136 * if (obj.isValid()) return true;  // OK
137 * if (true) {                      // OK
138 *     return true;
139 * } else                           // OK
140 *     return false;
141 * for (int i = 0; i &lt; 5; i++) {    // OK
142 *     ++count;
143 * }
144 * do                               // OK
145 *    ++count;
146 * while (false);
147 * for (int j = 0; j &lt; 10; j++);                   // violation, empty loop body not allowed
148 * for(int i = 0; i &lt; 10; value.incrementValue()); // violation, empty loop body not allowed
149 * while (counter &lt; 10)                 // OK
150 *    ++count;
151 * while (value.incrementValue() &lt; 5);  // violation, empty loop body not allowed
152 * switch (num) {
153 *   case 1: counter++; break;          // OK
154 * }
155 * while (obj.isValid()) return true;   // OK
156 * do this.notify(); while (o != null); // OK
157 * for (int i = 0; ; ) this.notify();   // OK
158 * </pre>
159 * <p>
160 * To configure the check to allow {@code case, default} single-line statements without braces:
161 * </p>
162 * <pre>
163 * &lt;module name=&quot;NeedBraces&quot;&gt;
164 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_CASE, LITERAL_DEFAULT&quot;/&gt;
165 *   &lt;property name=&quot;allowSingleLineStatement&quot; value=&quot;true&quot;/&gt;
166 * &lt;/module&gt;
167 * </pre>
168 * <p>
169 * Next statements won't be violated by check:
170 * </p>
171 * <pre>
172 * if (obj.isValid()) return true; // OK
173 * if (true) {                     // OK
174 *     return true;
175 * } else                          // OK
176 *     return false;
177 * for (int i = 0; i &lt; 5; i++) {   // OK
178 *      ++count;
179 * }
180 * do                              // OK
181 *     ++count;
182 * while (false);
183 * for (int j = 0; j &lt; 10; j++);   // OK
184 * for(int i = 0; i &lt; 10; value.incrementValue()); // OK
185 * while (counter &lt; 10)                            // OK
186 *    ++count;
187 * while (value.incrementValue() &lt; 5); // OK
188 * switch (num) {
189 *   case 1: counter++; break;         // OK
190 *   case 6: counter += 10; break;     // OK
191 *   default: counter = 100; break;    // OK
192 * }
193 * </pre>
194 * <p>
195 * To configure the check to allow loops ({@code while, for}) with empty bodies:
196 * </p>
197 * <pre>
198 * &lt;module name=&quot;NeedBraces&quot;&gt;
199 *   &lt;property name=&quot;allowEmptyLoopBody&quot; value=&quot;true&quot;/&gt;
200 *   &lt;property name=&quot;tokens&quot; value=&quot;LITERAL_WHILE, LITERAL_FOR&quot;/&gt;
201 * &lt;/module&gt;
202 * </pre>
203 * <p>
204 * Example:
205 * </p>
206 * <pre>
207 * if (obj.isValid()) return true; // OK
208 * if (true) {                     // OK
209 *     return true;
210 * } else                          // OK
211 *     return false;
212 * for (int i = 0; i &lt; 5; i++) {   // OK
213 *      ++count;
214 * }
215 * do                              // OK
216 *     ++count;
217 * while (false);
218 * for (int j = 0; j &lt; 10; j++);   // OK
219 * for(int i = 0; i &lt; 10; value.incrementValue()); // OK
220 * while (counter &lt; 10)           // violation, single-line statements not allowed without braces
221 *    ++count;
222 * while (value.incrementValue() &lt; 5); // OK
223 * switch (num) {
224 * case 1: counter++; break;           // OK
225 * }
226 * </pre>
227 * <p>
228 * To configure the check to lambdas:
229 * </p>
230 * <pre>
231 * &lt;module name=&quot;NeedBraces&quot;&gt;
232 *   &lt;property name=&quot;tokens&quot; value=&quot;LAMBDA&quot;/&gt;
233 *   &lt;property name=&quot;allowSingleLineStatement&quot; value=&quot;true&quot;/&gt;
234 * &lt;/module&gt;
235 * </pre>
236 * <p>
237 * Results in following:
238 * </p>
239 * <pre>
240 * allowedFuture.addCallback(result -&gt; assertEquals("Invalid response",
241 *   EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS), result), // violation, lambda spans 2 lines
242 *   ex -&gt; fail(ex.getMessage())); // OK
243 *
244 * allowedFuture.addCallback(result -&gt; {
245 *   return assertEquals("Invalid response",
246 *     EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS), result);
247 *   }, // OK
248 *   ex -&gt; fail(ex.getMessage()));
249 * </pre>
250 * <p>
251 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
252 * </p>
253 * <p>
254 * Violation Message Keys:
255 * </p>
256 * <ul>
257 * <li>
258 * {@code needBraces}
259 * </li>
260 * </ul>
261 *
262 * @since 3.0
263 */
264@StatelessCheck
265public class NeedBracesCheck extends AbstractCheck {
266
267    /**
268     * A key is pointing to the warning message text in "messages.properties"
269     * file.
270     */
271    public static final String MSG_KEY_NEED_BRACES = "needBraces";
272
273    /**
274     * Allow single-line statements without braces.
275     */
276    private boolean allowSingleLineStatement;
277
278    /**
279     * Allow loops with empty bodies.
280     */
281    private boolean allowEmptyLoopBody;
282
283    /**
284     * Setter to allow single-line statements without braces.
285     *
286     * @param allowSingleLineStatement Check's option for skipping single-line statements
287     */
288    public void setAllowSingleLineStatement(boolean allowSingleLineStatement) {
289        this.allowSingleLineStatement = allowSingleLineStatement;
290    }
291
292    /**
293     * Setter to allow loops with empty bodies.
294     *
295     * @param allowEmptyLoopBody Check's option for allowing loops with empty body.
296     */
297    public void setAllowEmptyLoopBody(boolean allowEmptyLoopBody) {
298        this.allowEmptyLoopBody = allowEmptyLoopBody;
299    }
300
301    @Override
302    public int[] getDefaultTokens() {
303        return new int[] {
304            TokenTypes.LITERAL_DO,
305            TokenTypes.LITERAL_ELSE,
306            TokenTypes.LITERAL_FOR,
307            TokenTypes.LITERAL_IF,
308            TokenTypes.LITERAL_WHILE,
309        };
310    }
311
312    @Override
313    public int[] getAcceptableTokens() {
314        return new int[] {
315            TokenTypes.LITERAL_DO,
316            TokenTypes.LITERAL_ELSE,
317            TokenTypes.LITERAL_FOR,
318            TokenTypes.LITERAL_IF,
319            TokenTypes.LITERAL_WHILE,
320            TokenTypes.LITERAL_CASE,
321            TokenTypes.LITERAL_DEFAULT,
322            TokenTypes.LAMBDA,
323        };
324    }
325
326    @Override
327    public int[] getRequiredTokens() {
328        return CommonUtil.EMPTY_INT_ARRAY;
329    }
330
331    @Override
332    public void visitToken(DetailAST ast) {
333        final boolean hasNoSlist = ast.findFirstToken(TokenTypes.SLIST) == null;
334        if (hasNoSlist && !isSkipStatement(ast) && isBracesNeeded(ast)) {
335            log(ast, MSG_KEY_NEED_BRACES, ast.getText());
336        }
337    }
338
339    /**
340     * Checks if token needs braces.
341     * Some tokens have additional conditions:
342     * <ul>
343     *     <li>{@link TokenTypes#LITERAL_FOR}</li>
344     *     <li>{@link TokenTypes#LITERAL_WHILE}</li>
345     *     <li>{@link TokenTypes#LITERAL_CASE}</li>
346     *     <li>{@link TokenTypes#LITERAL_DEFAULT}</li>
347     *     <li>{@link TokenTypes#LITERAL_ELSE}</li>
348     *     <li>{@link TokenTypes#LAMBDA}</li>
349     * </ul>
350     * For all others default value {@code true} is returned.
351     *
352     * @param ast token to check
353     * @return result of additional checks for specific token types,
354     *     {@code true} if there is no additional checks for token
355     */
356    private boolean isBracesNeeded(DetailAST ast) {
357        final boolean result;
358        switch (ast.getType()) {
359            case TokenTypes.LITERAL_FOR:
360            case TokenTypes.LITERAL_WHILE:
361                result = !isEmptyLoopBodyAllowed(ast);
362                break;
363            case TokenTypes.LITERAL_CASE:
364            case TokenTypes.LITERAL_DEFAULT:
365                result = hasUnbracedStatements(ast)
366                    && !isSwitchLabeledExpression(ast);
367                break;
368            case TokenTypes.LITERAL_ELSE:
369                result = ast.findFirstToken(TokenTypes.LITERAL_IF) == null;
370                break;
371            case TokenTypes.LAMBDA:
372                result = !isInSwitchRule(ast);
373                break;
374            default:
375                result = true;
376                break;
377        }
378        return result;
379    }
380
381    /**
382     * Checks if current loop has empty body and can be skipped by this check.
383     *
384     * @param ast for, while statements.
385     * @return true if current loop can be skipped by check.
386     */
387    private boolean isEmptyLoopBodyAllowed(DetailAST ast) {
388        return allowEmptyLoopBody && ast.findFirstToken(TokenTypes.EMPTY_STAT) != null;
389    }
390
391    /**
392     * Checks if switch member (case, default statements) has statements without curly braces.
393     *
394     * @param ast case, default statements.
395     * @return true if switch member has unbraced statements, false otherwise.
396     */
397    private static boolean hasUnbracedStatements(DetailAST ast) {
398        final DetailAST nextSibling = ast.getNextSibling();
399        boolean result = false;
400
401        if (isInSwitchRule(ast)) {
402            final DetailAST parent = ast.getParent();
403            result = parent.getLastChild().getType() != TokenTypes.SLIST;
404        }
405        else if (nextSibling != null
406            && nextSibling.getType() == TokenTypes.SLIST
407            && nextSibling.getFirstChild().getType() != TokenTypes.SLIST) {
408            result = true;
409        }
410        return result;
411    }
412
413    /**
414     * Checks if current statement can be skipped by "need braces" warning.
415     *
416     * @param statement if, for, while, do-while, lambda, else, case, default statements.
417     * @return true if current statement can be skipped by Check.
418     */
419    private boolean isSkipStatement(DetailAST statement) {
420        return allowSingleLineStatement && isSingleLineStatement(statement);
421    }
422
423    /**
424     * Checks if current statement is single-line statement, e.g.:
425     * <p>
426     * {@code
427     * if (obj.isValid()) return true;
428     * }
429     * </p>
430     * <p>
431     * {@code
432     * while (obj.isValid()) return true;
433     * }
434     * </p>
435     *
436     * @param statement if, for, while, do-while, lambda, else, case, default statements.
437     * @return true if current statement is single-line statement.
438     */
439    private static boolean isSingleLineStatement(DetailAST statement) {
440        final boolean result;
441
442        switch (statement.getType()) {
443            case TokenTypes.LITERAL_IF:
444                result = isSingleLineIf(statement);
445                break;
446            case TokenTypes.LITERAL_FOR:
447                result = isSingleLineFor(statement);
448                break;
449            case TokenTypes.LITERAL_DO:
450                result = isSingleLineDoWhile(statement);
451                break;
452            case TokenTypes.LITERAL_WHILE:
453                result = isSingleLineWhile(statement);
454                break;
455            case TokenTypes.LAMBDA:
456                result = !isInSwitchRule(statement)
457                    && isSingleLineLambda(statement);
458                break;
459            case TokenTypes.LITERAL_CASE:
460            case TokenTypes.LITERAL_DEFAULT:
461                result = isSingleLineSwitchMember(statement);
462                break;
463            default:
464                result = isSingleLineElse(statement);
465                break;
466        }
467
468        return result;
469    }
470
471    /**
472     * Checks if current while statement is single-line statement, e.g.:
473     * <p>
474     * {@code
475     * while (obj.isValid()) return true;
476     * }
477     * </p>
478     *
479     * @param literalWhile {@link TokenTypes#LITERAL_WHILE while statement}.
480     * @return true if current while statement is single-line statement.
481     */
482    private static boolean isSingleLineWhile(DetailAST literalWhile) {
483        boolean result = false;
484        if (literalWhile.getParent().getType() == TokenTypes.SLIST) {
485            final DetailAST block = literalWhile.getLastChild().getPreviousSibling();
486            result = TokenUtil.areOnSameLine(literalWhile, block);
487        }
488        return result;
489    }
490
491    /**
492     * Checks if current do-while statement is single-line statement, e.g.:
493     * <p>
494     * {@code
495     * do this.notify(); while (o != null);
496     * }
497     * </p>
498     *
499     * @param literalDo {@link TokenTypes#LITERAL_DO do-while statement}.
500     * @return true if current do-while statement is single-line statement.
501     */
502    private static boolean isSingleLineDoWhile(DetailAST literalDo) {
503        boolean result = false;
504        if (literalDo.getParent().getType() == TokenTypes.SLIST) {
505            final DetailAST block = literalDo.getFirstChild();
506            result = TokenUtil.areOnSameLine(block, literalDo);
507        }
508        return result;
509    }
510
511    /**
512     * Checks if current for statement is single-line statement, e.g.:
513     * <p>
514     * {@code
515     * for (int i = 0; ; ) this.notify();
516     * }
517     * </p>
518     *
519     * @param literalFor {@link TokenTypes#LITERAL_FOR for statement}.
520     * @return true if current for statement is single-line statement.
521     */
522    private static boolean isSingleLineFor(DetailAST literalFor) {
523        boolean result = false;
524        if (literalFor.getLastChild().getType() == TokenTypes.EMPTY_STAT) {
525            result = true;
526        }
527        else if (literalFor.getParent().getType() == TokenTypes.SLIST) {
528            result = TokenUtil.areOnSameLine(literalFor, literalFor.getLastChild());
529        }
530        return result;
531    }
532
533    /**
534     * Checks if current if statement is single-line statement, e.g.:
535     * <p>
536     * {@code
537     * if (obj.isValid()) return true;
538     * }
539     * </p>
540     *
541     * @param literalIf {@link TokenTypes#LITERAL_IF if statement}.
542     * @return true if current if statement is single-line statement.
543     */
544    private static boolean isSingleLineIf(DetailAST literalIf) {
545        boolean result = false;
546        if (literalIf.getParent().getType() == TokenTypes.SLIST) {
547            final DetailAST literalIfLastChild = literalIf.getLastChild();
548            final DetailAST block;
549            if (literalIfLastChild.getType() == TokenTypes.LITERAL_ELSE) {
550                block = literalIfLastChild.getPreviousSibling();
551            }
552            else {
553                block = literalIfLastChild;
554            }
555            final DetailAST ifCondition = literalIf.findFirstToken(TokenTypes.EXPR);
556            result = TokenUtil.areOnSameLine(ifCondition, block);
557        }
558        return result;
559    }
560
561    /**
562     * Checks if current lambda statement is single-line statement, e.g.:
563     * <p>
564     * {@code
565     * Runnable r = () -> System.out.println("Hello, world!");
566     * }
567     * </p>
568     *
569     * @param lambda {@link TokenTypes#LAMBDA lambda statement}.
570     * @return true if current lambda statement is single-line statement.
571     */
572    private static boolean isSingleLineLambda(DetailAST lambda) {
573        final DetailAST lastLambdaToken = getLastLambdaToken(lambda);
574        return TokenUtil.areOnSameLine(lambda, lastLambdaToken);
575    }
576
577    /**
578     * Looks for the last token in lambda.
579     *
580     * @param lambda token to check.
581     * @return last token in lambda
582     */
583    private static DetailAST getLastLambdaToken(DetailAST lambda) {
584        DetailAST node = lambda;
585        do {
586            node = node.getLastChild();
587        } while (node.getLastChild() != null);
588        return node;
589    }
590
591    /**
592     * Checks if current ast's parent is a switch rule, e.g.:
593     * <p>
594     * {@code
595     * case 1 ->  monthString = "January";
596     * }
597     * </p>
598     *
599     * @param ast the ast to check.
600     * @return true if current ast belongs to a switch rule.
601     */
602    private static boolean isInSwitchRule(DetailAST ast) {
603        return ast.getParent().getType() == TokenTypes.SWITCH_RULE;
604    }
605
606    /**
607     * Checks if current expression is a switch labeled expression. If so,
608     * braces are not allowed e.g.:
609     * <p>
610     * {@code
611     * case 1 -> 4;
612     * }
613     * </p>
614     *
615     * @param ast the ast to check
616     * @return true if current expression is a switch labeled expression.
617     */
618    private static boolean isSwitchLabeledExpression(DetailAST ast) {
619        final DetailAST parent = ast.getParent();
620        return switchRuleHasSingleExpression(parent);
621    }
622
623    /**
624     * Checks if current switch labeled expression contains only a single expression.
625     *
626     * @param switchRule {@link TokenTypes#SWITCH_RULE}.
627     * @return true if current switch rule has a single expression.
628     */
629    private static boolean switchRuleHasSingleExpression(DetailAST switchRule) {
630        final DetailAST possibleExpression = switchRule.findFirstToken(TokenTypes.EXPR);
631        return possibleExpression != null
632                && possibleExpression.getFirstChild().getFirstChild() == null;
633    }
634
635    /**
636     * Checks if switch member (case or default statement) in a switch rule or
637     * case group is on a single line.
638     *
639     * @param statement {@link TokenTypes#LITERAL_CASE case statement} or
640     *     {@link TokenTypes#LITERAL_DEFAULT default statement}.
641     * @return true if current switch member is single-line statement.
642     */
643    private static boolean isSingleLineSwitchMember(DetailAST statement) {
644        final boolean result;
645        if (isInSwitchRule(statement)) {
646            result = isSingleLineSwitchRule(statement);
647        }
648        else {
649            result = isSingleLineCaseGroup(statement);
650        }
651        return result;
652    }
653
654    /**
655     * Checks if switch member in case group (case or default statement)
656     * is single-line statement, e.g.:
657     * <p>
658     * {@code
659     * case 1: System.out.println("case one"); break;
660     * case 2: System.out.println("case two"); break;
661     * case 3: ;
662     * default: System.out.println("default"); break;
663     * }
664     * </p>
665     *
666     *
667     * @param ast {@link TokenTypes#LITERAL_CASE case statement} or
668     *     {@link TokenTypes#LITERAL_DEFAULT default statement}.
669     * @return true if current switch member is single-line statement.
670     */
671    private static boolean isSingleLineCaseGroup(DetailAST ast) {
672        return Optional.of(ast)
673            .map(DetailAST::getNextSibling)
674            .map(DetailAST::getLastChild)
675            .map(lastToken -> TokenUtil.areOnSameLine(ast, lastToken))
676            .orElse(true);
677    }
678
679    /**
680     * Checks if switch member in switch rule (case or default statement) is
681     * single-line statement, e.g.:
682     * <p>
683     * {@code
684     * case 1 -> System.out.println("case one");
685     * case 2 -> System.out.println("case two");
686     * default -> System.out.println("default");
687     * }
688     * </p>
689     *
690     * @param ast {@link TokenTypes#LITERAL_CASE case statement} or
691     *            {@link TokenTypes#LITERAL_DEFAULT default statement}.
692     * @return true if current switch label is single-line statement.
693     */
694    private static boolean isSingleLineSwitchRule(DetailAST ast) {
695        final DetailAST lastSibling = ast.getParent().getLastChild();
696        return TokenUtil.areOnSameLine(ast, lastSibling);
697    }
698
699    /**
700     * Checks if current else statement is single-line statement, e.g.:
701     * <p>
702     * {@code
703     * else doSomeStuff();
704     * }
705     * </p>
706     *
707     * @param literalElse {@link TokenTypes#LITERAL_ELSE else statement}.
708     * @return true if current else statement is single-line statement.
709     */
710    private static boolean isSingleLineElse(DetailAST literalElse) {
711        final DetailAST block = literalElse.getFirstChild();
712        return TokenUtil.areOnSameLine(literalElse, block);
713    }
714
715}