1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26
27 /**
28 * <p>
29 * Finds nested blocks (blocks that are used freely in the code).
30 * </p>
31 * <p>
32 * Rationale: Nested blocks are often leftovers from the
33 * debugging process, they confuse the reader.
34 * </p>
35 * <p>
36 * For example, this check finds the obsolete braces in
37 * </p>
38 * <pre>
39 * public void guessTheOutput()
40 * {
41 * int whichIsWhich = 0;
42 * {
43 * whichIsWhich = 2;
44 * }
45 * System.out.println("value = " + whichIsWhich);
46 * }
47 * </pre>
48 * <p>
49 * and debugging / refactoring leftovers such as
50 * </p>
51 * <pre>
52 * // if (conditionThatIsNotUsedAnyLonger)
53 * {
54 * System.out.println("unconditional");
55 * }
56 * </pre>
57 * <p>
58 * A case in a switch statement does not implicitly form a block.
59 * Thus, to be able to introduce local variables that have case scope
60 * it is necessary to open a nested block. This is supported, set
61 * the allowInSwitchCase property to true and include all statements
62 * of the case in the block.
63 * </p>
64 * <ul>
65 * <li>
66 * Property {@code allowInSwitchCase} - Allow nested blocks if they are the
67 * only child of a switch case.
68 * Type is {@code boolean}.
69 * Default value is {@code false}.
70 * </li>
71 * </ul>
72 * <p>
73 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
74 * </p>
75 * <p>
76 * Violation Message Keys:
77 * </p>
78 * <ul>
79 * <li>
80 * {@code block.nested}
81 * </li>
82 * </ul>
83 *
84 * @since 3.1
85 */
86 @StatelessCheck
87 public class AvoidNestedBlocksCheck extends AbstractCheck {
88
89 /**
90 * A key is pointing to the warning message text in "messages.properties"
91 * file.
92 */
93 public static final String MSG_KEY_BLOCK_NESTED = "block.nested";
94
95 /**
96 * Allow nested blocks if they are the only child of a switch case.
97 */
98 private boolean allowInSwitchCase;
99
100 @Override
101 public int[] getDefaultTokens() {
102 return getRequiredTokens();
103 }
104
105 @Override
106 public int[] getAcceptableTokens() {
107 return getRequiredTokens();
108 }
109
110 @Override
111 public int[] getRequiredTokens() {
112 return new int[] {TokenTypes.SLIST};
113 }
114
115 @Override
116 public void visitToken(DetailAST ast) {
117 final DetailAST parent = ast.getParent();
118 if (parent.getType() == TokenTypes.SLIST
119 && (!allowInSwitchCase || hasSiblings(ast))) {
120 log(ast, MSG_KEY_BLOCK_NESTED);
121 }
122 }
123
124 /**
125 * Checks whether the AST node has any siblings or not.
126 *
127 * @param ast node to examine
128 * @return {@code true} if the node has one or more siblings
129 */
130 private static boolean hasSiblings(DetailAST ast) {
131 return ast.getPreviousSibling() != null || ast.getNextSibling() != null;
132 }
133
134 /**
135 * Setter to allow nested blocks if they are the only child of a switch case.
136 *
137 * @param allowInSwitchCase whether nested blocks are allowed
138 * if they are the only child of a switch case.
139 * @since 3.2
140 */
141 public void setAllowInSwitchCase(boolean allowInSwitchCase) {
142 this.allowInSwitchCase = allowInSwitchCase;
143 }
144
145 }