diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheck.java
index c9d604c0f..2a4cef906 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheck.java
@@ -49,14 +49,54 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes;
* <property name="tokens" value="LITERAL_IF, LITERAL_ELSE"/>
* </module>
*
- * Check has an option allowSingleLineIf which allows one line
- * if-statements without braces, e.g.:
+ * Check has an option allowSingleLineStatement which allows single-line
+ * statements without braces, e.g.:
*
*
* if (obj.isValid()) return true;
*
*
- *
+ *
+ *
+ * while (obj.isValid()) return true;
+ *
+ *
+ *
+ *
+ * do this.notify(); while (o != null);
+ *
+ *
+ *
+ *
+ * for (int i = 0; ; ) this.notify();
+ *
+ *
+ *
+ * To configure the Check to allow case, default single-line statements
+ * without braces:
+ *
+ *
+ *
+ * <module name="NeedBraces">
+ * <property name="tokens" value="LITERAL_CASE, LITERAL_DEFAULT"/>
+ * <property name="allowSingleLineStatement" value="true"/>
+ * </module>
+ *
+ *
+ *
+ * Such statements would be allowed:
+ *
+ *
+ *
+ *
+ * switch (num) {
+ * case 1: counter++; break; // OK
+ * case 6: counter += 10; break; // OK
+ * default: counter = 100; break; // OK
+ * }
+ *
+ *
+ *
*
* @author Rick Giles
* @author Aleksey Nesterenko
@@ -64,9 +104,9 @@ import com.puppycrawl.tools.checkstyle.api.TokenTypes;
public class NeedBracesCheck extends Check
{
/**
- * Check's option for skipping single-line if-statements.
+ * Check's option for skipping single-line statements.
*/
- private boolean allowSingleLineIf;
+ private boolean allowSingleLineStatement;
/**
* A key is pointing to the warning message text in "messages.properties"
@@ -76,11 +116,11 @@ public class NeedBracesCheck extends Check
/**
* Setter.
- * @param allowSingleLineIf Check's option for skipping single-line if-statements
+ * @param allowSingleLineStatement Check's option for skipping single-line statements
*/
- public void setAllowSingleLineIf(boolean allowSingleLineIf)
+ public void setAllowSingleLineStatement(boolean allowSingleLineStatement)
{
- this.allowSingleLineIf = allowSingleLineIf;
+ this.allowSingleLineStatement = allowSingleLineStatement;
}
@Override
@@ -120,45 +160,252 @@ public class NeedBracesCheck extends Check
{
isElseIf = true;
}
- boolean skipStatement = false;
- if (ast.getType() == TokenTypes.LITERAL_IF) {
- skipStatement = isSkipIfBlock(ast);
- }
+
+ final boolean skipStatement = isSkipStatement(ast);
+
if ((slistAST == null) && !isElseIf && !skipStatement) {
log(ast.getLineNo(), MSG_KEY_NEED_BRACES, ast.getText());
}
}
/**
- * Checks if current if-block can be skipped by "need braces" warning.
- * @param literalIf {@link TokenTypes#LITERAL_IF LITERAL_IF}
- * @return true if current if block can be skipped by Check
+ * Checks if current statement can be skipped by "need braces" warning.
+ * @param statement if, for, while, do-while, lambda, else, case, default statements.
+ * @return true if current statement can be skipped by Check.
*/
- private boolean isSkipIfBlock(DetailAST literalIf)
+ private boolean isSkipStatement(DetailAST statement)
{
- return allowSingleLineIf && isSingleLineIf(literalIf);
+ return allowSingleLineStatement && isSingleLineStatement(statement);
}
/**
- * Checks if current if-statement is single-line statement, e.g.:
+ * Checks if current statement is single-line statement, e.g.:
*
*
* if (obj.isValid()) return true;
*
*
- * @param literalIf {@link TokenTypes#LITERAL_IF LITERAL_IF}
- * @return true if current if-statement is single-line statement
+ *
+ *
+ * while (obj.isValid()) return true;
+ *
+ *
+ * @param statement if, for, while, do-while, lambda, else, case, default statements.
+ * @return true if current statement is single-line statement.
+ */
+ private static boolean isSingleLineStatement(DetailAST statement)
+ {
+ boolean result = false;
+ switch (statement.getType()) {
+ case TokenTypes.LITERAL_IF:
+ result = isSingleLineIf(statement);
+ break;
+ case TokenTypes.LITERAL_FOR:
+ result = isSingleLineFor(statement);
+ break;
+ case TokenTypes.LITERAL_DO:
+ result = isSingleLineDoWhile(statement);
+ break;
+ case TokenTypes.LITERAL_WHILE:
+ result = isSingleLineWhile(statement);
+ break;
+ case TokenTypes.LAMBDA:
+ result = isSingleLineLambda(statement);
+ break;
+ case TokenTypes.LITERAL_CASE:
+ result = isSingleLineCase(statement);
+ break;
+ case TokenTypes.LITERAL_DEFAULT:
+ result = isSingleLineDefault(statement);
+ break;
+ case TokenTypes.LITERAL_ELSE:
+ result = isSingleLineElse(statement);
+ break;
+ default:
+ final String exceptionMsg = statement.getText();
+ throw new IllegalArgumentException("Unsupported token: " + exceptionMsg);
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current while statement is single-line statement, e.g.:
+ *
+ *
+ * while (obj.isValid()) return true;
+ *
+ *
+ * @param literalWhile {@link TokenTypes#LITERAL_WHILE while statement}.
+ * @return true if current while statement is single-line statement.
+ */
+ private static boolean isSingleLineWhile(DetailAST literalWhile)
+ {
+ boolean result = false;
+ if (literalWhile.getParent().getType() == TokenTypes.SLIST
+ && literalWhile.getLastChild().getType() != TokenTypes.SLIST)
+ {
+ final DetailAST block = literalWhile.getLastChild().getPreviousSibling();
+ result = literalWhile.getLineNo() == block.getLineNo();
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current do-while statement is single-line statement, e.g.:
+ *
+ *
+ * do this.notify(); while (o != null);
+ *
+ *
+ * @param literalDo {@link TokenTypes#LITERAL_DO do-while statement}.
+ * @return true if current do-while statement is single-line statement.
+ */
+ private static boolean isSingleLineDoWhile(DetailAST literalDo)
+ {
+ boolean result = false;
+ if (literalDo.getParent().getType() == TokenTypes.SLIST
+ && literalDo.getFirstChild().getType() != TokenTypes.SLIST)
+ {
+ final DetailAST block = literalDo.getFirstChild();
+ result = block.getLineNo() == literalDo.getLineNo();
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current for statement is single-line statement, e.g.:
+ *
+ *
+ * for (int i = 0; ; ) this.notify();
+ *
+ *
+ * @param literalFor {@link TokenTypes#LITERAL_FOR for statement}.
+ * @return true if current for statement is single-line statement.
+ */
+ private static boolean isSingleLineFor(DetailAST literalFor)
+ {
+ boolean result = false;
+ if (literalFor.getLastChild().getType() == TokenTypes.EMPTY_STAT) {
+ result = true;
+ }
+ else if (literalFor.getParent().getType() == TokenTypes.SLIST
+ && literalFor.getLastChild().getType() != TokenTypes.SLIST)
+ {
+ final DetailAST block = literalFor.findFirstToken(TokenTypes.EXPR);
+ if (block != null) {
+ result = literalFor.getLineNo() == block.getLineNo();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current if statement is single-line statement, e.g.:
+ *
+ *
+ * if (obj.isValid()) return true;
+ *
+ *
+ * @param literalIf {@link TokenTypes#LITERAL_IF if statement}.
+ * @return true if current if statement is single-line statement.
*/
private static boolean isSingleLineIf(DetailAST literalIf)
{
boolean result = false;
- final DetailAST ifBlock = literalIf.getLastChild();
- final DetailAST lastElementInIfBlock = ifBlock.getLastChild();
- if (lastElementInIfBlock != null
- && lastElementInIfBlock.getFirstChild() == null
- && literalIf.getLineNo() == lastElementInIfBlock.getLineNo())
- {
- result = true;
+ final DetailAST ifCondition = literalIf.findFirstToken(TokenTypes.EXPR);
+ if (literalIf.getParent().getType() == TokenTypes.SLIST) {
+ DetailAST block = literalIf.getLastChild();
+ if (block.getType() != TokenTypes.LITERAL_RETURN) {
+ block = literalIf.getLastChild().getPreviousSibling();
+ }
+ result = ifCondition.getLineNo() == block.getLineNo();
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current lambda statement is single-line statement, e.g.:
+ *
+ *
+ * Runnable r = () -> System.out.println("Hello, world!");
+ *
+ *
+ * @param lambda {@link TokenTypes#LAMBDA lambda statement}.
+ * @return true if current lambda statement is single-line statement.
+ */
+ private static boolean isSingleLineLambda(DetailAST lambda)
+ {
+ boolean result = false;
+ final DetailAST block = lambda.getLastChild();
+ if (block.getType() != TokenTypes.SLIST) {
+ result = lambda.getLineNo() == block.getLineNo();
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current case statement is single-line statement, e.g.:
+ *
+ *
+ * case 1: dosomeStuff(); break;
+ * case 2: dosomeStuff(); break;
+ *
+ *
+ * @param literalCase {@link TokenTypes#LITERAL_CASE case statement}.
+ * @return true if current case statement is single-line statement.
+ */
+ private static boolean isSingleLineCase(DetailAST literalCase)
+ {
+ boolean result = false;
+ final DetailAST slist = literalCase.getNextSibling();
+ final DetailAST block = slist.getFirstChild();
+ if (block.getType() != TokenTypes.SLIST) {
+ final DetailAST caseBreak = slist.findFirstToken(TokenTypes.LITERAL_BREAK);
+ final boolean atOneLine = literalCase.getLineNo() == block.getLineNo();
+ if (caseBreak != null) {
+ result = atOneLine && (block.getLineNo() == caseBreak.getLineNo());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current default statement is single-line statement, e.g.:
+ *
+ *
+ * default: doSomeStuff();
+ *
+ *
+ * @param literalDefault {@link TokenTypes#LITERAL_DEFAULT default statement}.
+ * @return true if current default statement is single-line statement.
+ */
+ private static boolean isSingleLineDefault(DetailAST literalDefault)
+ {
+ boolean result = false;
+ final DetailAST slist = literalDefault.getNextSibling();
+ final DetailAST block = slist.getFirstChild();
+ if (block.getType() != TokenTypes.SLIST) {
+ result = literalDefault.getLineNo() == block.getLineNo();
+ }
+ return result;
+ }
+
+ /**
+ * Checks if current else statement is single-line statement, e.g.:
+ *
+ *
+ * else doSomeStuff();
+ *
+ *
+ * @param literalElse {@link TokenTypes#LITERAL_ELSE else statement}.
+ * @return true if current else statement is single-line statement.
+ */
+ private static boolean isSingleLineElse(DetailAST literalElse)
+ {
+ boolean result = false;
+ final DetailAST block = literalElse.getFirstChild();
+ if (block.getType() != TokenTypes.SLIST) {
+ result = literalElse.getLineNo() == block.getLineNo();
}
return result;
}
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheckTest.java
index 527de006c..b02bfc1fe 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/blocks/NeedBracesCheckTest.java
@@ -18,8 +18,11 @@
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks.blocks;
+import java.io.File;
+
import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
+
import org.junit.Test;
import static com.puppycrawl.tools.checkstyle.checks.blocks.NeedBracesCheck.MSG_KEY_NEED_BRACES;
@@ -54,15 +57,47 @@ public class NeedBracesCheckTest extends BaseCheckTestSupport
}
@Test
- public void testSigleLineIfBlock() throws Exception
+ public void testSigleLineStatements() throws Exception
{
final DefaultConfiguration checkConfig =
createCheckConfig(NeedBracesCheck.class);
- checkConfig.addAttribute("allowSingleLineIf", "true");
+ checkConfig.addAttribute("allowSingleLineStatement", "true");
final String[] expected = {
"23: " + getCheckMessage(MSG_KEY_NEED_BRACES, "if"),
"29: " + getCheckMessage(MSG_KEY_NEED_BRACES, "if"),
+ "38: " + getCheckMessage(MSG_KEY_NEED_BRACES, "if"),
+ "46: " + getCheckMessage(MSG_KEY_NEED_BRACES, "while"),
+ "53: " + getCheckMessage(MSG_KEY_NEED_BRACES, "do"),
+ "59: " + getCheckMessage(MSG_KEY_NEED_BRACES, "for"),
};
- verify(checkConfig, getPath("InputBracesSingleLineIfBlock.java"), expected);
+ verify(checkConfig, getPath("InputBracesSingleLineStatements.java"), expected);
+ }
+
+ @Test
+ public void testSigleLineLambda() throws Exception
+ {
+ final DefaultConfiguration checkConfig =
+ createCheckConfig(NeedBracesCheck.class);
+ checkConfig.addAttribute("tokens", "LAMBDA");
+ checkConfig.addAttribute("allowSingleLineStatement", "true");
+ final String[] expected = {
+ };
+ verify(checkConfig, new File("src/test/resources-noncompilable/com/puppycrawl/"
+ + "tools/checkstyle/blocks/InputSingleLineLambda.java").getCanonicalPath(),
+ expected);
+ }
+
+ @Test
+ public void testSigleLineCaseDefault() throws Exception
+ {
+ final DefaultConfiguration checkConfig =
+ createCheckConfig(NeedBracesCheck.class);
+ checkConfig.addAttribute("tokens", "LITERAL_CASE, LITERAL_DEFAULT");
+ checkConfig.addAttribute("allowSingleLineStatement", "true");
+ final String[] expected = {
+ "69: " + getCheckMessage(MSG_KEY_NEED_BRACES, "case"),
+ "72: " + getCheckMessage(MSG_KEY_NEED_BRACES, "case"),
+ };
+ verify(checkConfig, getPath("InputBracesSingleLineStatements.java"), expected);
}
}
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/blocks/InputSingleLineLambda.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/blocks/InputSingleLineLambda.java
new file mode 100644
index 000000000..0ed454e45
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/blocks/InputSingleLineLambda.java
@@ -0,0 +1,7 @@
+//Compilable with Java8
+package com.puppycrawl.tools.checkstyle.blocks;
+public class InputSingleLineLambda {
+
+ static Runnable r1 = ()->System.out.println("Hello world one!");
+ static Runnable r2 = () -> System.out.println("Hello world two!");
+}
\ No newline at end of file
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineIfBlock.java b/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineIfBlock.java
deleted file mode 100644
index 95403705c..000000000
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineIfBlock.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.puppycrawl.tools.checkstyle;
-
-public class InputBracesSingleLineIfBlock
-{
- private static class SomeClass {
- boolean flag = true;
- private static boolean test(boolean k) {
- return k;
- }
- }
-
- private int foo() {
- if (SomeClass.test(true) == true) return 4; //No warning if 'mAllowSingleLineIf' is true
- return 0;
- }
-
- private int foo1() {
- if (SomeClass.test(true) == true) return 4; int k = 3; //No warning if 'mAllowSingleLineIf' is true
- return 0;
- }
-
- private int foo2() {
- if (SomeClass.test(true) == true) //Warning, not single-line if-statement
- return 4;
- return 0;
- }
-
- private int foo3() {
- if (SomeClass.test(true) == true) if (true) return 4; //Warning, complex block
- return 0;
- }
-}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineStatements.java b/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineStatements.java
new file mode 100644
index 000000000..1c2b54387
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/InputBracesSingleLineStatements.java
@@ -0,0 +1,86 @@
+package com.puppycrawl.tools.checkstyle;
+
+public class InputBracesSingleLineStatements
+{
+ private static class SomeClass {
+ boolean flag = true;
+ private static boolean test(boolean k) {
+ return k;
+ }
+ }
+
+ private int foo() {
+ if (SomeClass.test(true) == true) return 4; //No warning if 'mAllowSingleLineIf' is true
+ return 0;
+ }
+
+ private int foo1() {
+ if (SomeClass.test(true) == true) return 4; int k = 3; //No warning if 'mAllowSingleLineIf' is true
+ return 0;
+ }
+
+ private int foo2() {
+ if (SomeClass.test(true) == true) //Warning, not single-line if-statement
+ return 4;
+ return 0;
+ }
+
+ private int foo3() {
+ if (SomeClass.test(true) == true) if (true) return 4; //Warning, complex block
+ return 0;
+ }
+
+ private void foo(Object o) {
+ if (o != null) this.notify();
+ }
+
+ private void foo2(Object o) {
+ if (o != null)
+ this.notify();
+ }
+
+ private void loopTest(Object o) {
+ while (o != null) {
+ this.notify();
+ }
+ while (o != null)
+ this.notify();
+ while (o != null) this.notify();
+ do {
+ this.notify();
+ } while (o != null);
+ do this.notify(); while (o != null);
+ do
+ this.notify();
+ while (o != null);
+ for (int i = 0; i < 10; i++) {
+ this.notify();
+ }
+ for (int i = 0; i < 10; i++)
+ this.notify();
+ for (int i = 0; ; ) this.notify();
+ }
+
+ private int getSmth(int num)
+ {
+ int counter = 0;
+ switch (num) {
+ case 1: counter++; break;
+ case 2:
+ counter += 2;
+ break;
+ case 3:
+ counter += 3;
+ break;
+ case 6: counter += 10; break;
+ default: counter = 100; break;
+ }
+ return counter;
+ }
+
+ private void testElse(int k) {
+ if (k == 4) System.out.println("yes");
+ else System.out.println("no");
+ for (;;);
+ }
+}
diff --git a/src/xdocs/config_blocks.xml b/src/xdocs/config_blocks.xml
index 8ad21fd22..35ef9eefc 100644
--- a/src/xdocs/config_blocks.xml
+++ b/src/xdocs/config_blocks.xml
@@ -337,8 +337,8 @@ try {
all tokens
- allowSingleLineIf
- allow one line if statements
+ allowSingleLineStatement
+ allows single-line statements without braces
boolean
false
@@ -362,13 +362,46 @@ try {
- To configure the check to allow one line if statements without braces:
+ To configure the check to allow single-line statements
+ (if, while, do-while, for) without braces:
<module name="NeedBraces">
- <property name="allowSingleLineIf" value="FALSE"/>
+ <property name="allowSingleLineStatement" value="true"/>
</module>
+
+
+ Next statements won't be violated by Check:
+
+
+if (obj.isValid()) return true; // OK
+while (obj.isValid()) return true; // OK
+do this.notify(); while (o != null); // OK
+for (int i = 0; ; ) this.notify(); // OK
+
+
+
+ To configure the Check to allow case, default single-line statements
+ without braces:
+
+
+<module name="NeedBraces">
+ <property name="tokens" value="LITERAL_CASE, LITERAL_DEFAULT"/>
+ <property name="allowSingleLineStatement" value="true"/>
+</module>
+
+
+
+ Next statements won't be violated by Check:
+
+
+switch (num) {
+ case 1: counter++; break; // OK
+ case 6: counter += 10; break; // OK
+ default: counter = 100; break; // OK
+}
+