Java: Detect potential dangling 'else' in "code block contains single statement" inspection (IDEA-174415)

This commit is contained in:
Pavel Dolgov
2017-07-17 17:08:38 +03:00
parent c897be5542
commit dcef98fe6e
5 changed files with 219 additions and 170 deletions
@@ -98,7 +98,7 @@ public class SingleStatementInBlockInspection extends BaseInspection {
protected boolean isApplicable(PsiStatement body) {
if (body instanceof PsiBlockStatement) {
final PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements();
if (statements.length == 1 && !(statements[0] instanceof PsiDeclarationStatement)) {
if (statements.length == 1 && !(statements[0] instanceof PsiDeclarationStatement) && !isDanglingElseProblem(statements[0], body)) {
final PsiFile file = body.getContainingFile();
//this inspection doesn't work in JSP files, as it can't tell about tags
// inside the braces
@@ -125,6 +125,45 @@ public class SingleStatementInBlockInspection extends BaseInspection {
}
return null;
}
/**
* See JLS paragraphs 14.5, 14.9
*/
private static boolean isDanglingElseProblem(@Nullable PsiStatement statement, @NotNull PsiStatement outerStatement) {
return hasShortIf(statement) && hasPotentialDanglingElse(outerStatement);
}
private static boolean hasShortIf(@Nullable PsiStatement statement) {
if (statement instanceof PsiIfStatement) {
final PsiStatement elseBranch = ((PsiIfStatement)statement).getElseBranch();
return elseBranch == null || hasShortIf(elseBranch);
}
if (statement instanceof PsiLabeledStatement) {
return hasShortIf(((PsiLabeledStatement)statement).getStatement());
}
if (statement instanceof PsiWhileStatement || statement instanceof PsiForStatement || statement instanceof PsiForeachStatement) {
return hasShortIf(((PsiLoopStatement)statement).getBody());
}
return false;
}
private static boolean hasPotentialDanglingElse(@NotNull PsiStatement statement) {
final PsiElement parent = statement.getParent();
if (parent instanceof PsiIfStatement) {
final PsiIfStatement ifStatement = (PsiIfStatement)parent;
if (ifStatement.getThenBranch() == statement && ifStatement.getElseBranch() != null) {
return true;
}
return hasPotentialDanglingElse(ifStatement);
}
if (parent instanceof PsiLabeledStatement ||
parent instanceof PsiWhileStatement ||
parent instanceof PsiForStatement ||
parent instanceof PsiForeachStatement) {
return hasPotentialDanglingElse((PsiStatement)parent);
}
return false;
}
}
private static class SingleStatementInBlockFix extends InspectionGadgetsFix {
@@ -1,63 +0,0 @@
class T {
void f(String[] a) {
for (String s : a) {
System.out.println(s);
}
if (a.length == 0) {
System.out.println("no");
} else {
System.out.println(a.length);
}
if (a.length == 0) {
System.out.println("no");
}
if (a.length == 0) {
} else {
System.out.println(a.length);
}
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
int j = 0;
do {
System.out.println(a[j++]);
}
while (j < a.length);
int k = 0;
while (k < a.length) {
System.out.println(a[k++]);
}
}
void ff(String[] a) {
if (a.length != 0) {
for (String arg : a) {
if (arg.length() > 1) {
for (int i = 0; i < arg.length(); i++) {
System.out.println(arg.charAt(i));
}
} else {
System.out.println(0);
}
}
} else {
System.out.println("no");
}
}
void decl(String[] a) {
if (a.length == 1) {
String t = a[0];
}
for (int i = 0; i < a.length; i++) {
String t = a[i];
}
}
}
@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<problems>
<problem>
<file>SingleStatement.java</file>
<line>4</line>
<problem_class>Code block contains single statement</problem_class>
<description>'for' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>8</line>
<problem_class>Code block contains single statement</problem_class>
<description>'if' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>10</line>
<problem_class>Code block contains single statement</problem_class>
<description>'else' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>14</line>
<problem_class>Code block contains single statement</problem_class>
<description>'if' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>19</line>
<problem_class>Code block contains single statement</problem_class>
<description>'else' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>23</line>
<problem_class>Code block contains single statement</problem_class>
<description>'for' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>28</line>
<problem_class>Code block contains single statement</problem_class>
<description>'do' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>34</line>
<problem_class>Code block contains single statement</problem_class>
<description>'while' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>40</line>
<problem_class>Code block contains single statement</problem_class>
<description>'if' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>41</line>
<problem_class>Code block contains single statement</problem_class>
<description>'for' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>42</line>
<problem_class>Code block contains single statement</problem_class>
<description>'if' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>43</line>
<problem_class>Code block contains single statement</problem_class>
<description>'for' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>46</line>
<problem_class>Code block contains single statement</problem_class>
<description>'else' contains single statement</description>
</problem>
<problem>
<file>SingleStatement.java</file>
<line>50</line>
<problem_class>Code block contains single statement</problem_class>
<description>'else' contains single statement</description>
</problem>
</problems>
@@ -0,0 +1,169 @@
class T {
void f(String[] a) {
<warning descr="'for' contains single statement">for</warning> (String s : a) {
System.out.println(s);
}
<warning descr="'if' contains single statement">if</warning> (a.length == 0) {
System.out.println("no");
} <warning descr="'else' contains single statement">else</warning> {
System.out.println(a.length);
}
<warning descr="'if' contains single statement">if</warning> (a.length == 0) {
System.out.println("no");
}
if (a.length == 0) {
} <warning descr="'else' contains single statement">else</warning> {
System.out.println(a.length);
}
<warning descr="'for' contains single statement">for</warning> (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
int j = 0;
<warning descr="'do' contains single statement">do</warning> {
System.out.println(a[j++]);
}
while (j < a.length);
int k = 0;
<warning descr="'while' contains single statement">while</warning> (k < a.length) {
System.out.println(a[k++]);
}
}
void nested(String[] a) {
<warning descr="'if' contains single statement">if</warning> (a.length != 0) {
<warning descr="'for' contains single statement">for</warning> (String arg : a) {
<warning descr="'if' contains single statement">if</warning> (arg.length() > 1) {
<warning descr="'for' contains single statement">for</warning> (int i = 0; i < arg.length(); i++) {
System.out.println(arg.charAt(i));
}
} <warning descr="'else' contains single statement">else</warning> {
System.out.println(0);
}
}
} <warning descr="'else' contains single statement">else</warning> {
System.out.println("no");
}
}
void decl(String[] a) {
if (a.length == 1) {
String t = a[0];
}
for (int i = 0; i < a.length; i++) {
String t = a[i];
}
}
void labeled(String[] a) {
OuterIf:
<warning descr="'if' contains single statement">if</warning> (a != null) {
OuterFor:
<warning descr="'for' contains single statement">for</warning> (String s : a) {
InnerFor:
<warning descr="'for' contains single statement">for</warning> (int i = 0; i < s.length(); i++) {
InnerIf:
<warning descr="'if' contains single statement">if</warning> (s.charAt(i) == ' ') {
break OuterFor;
}
}
}
}
}
void danglingElse(Object[] a) {
if (a != null) {
if (a.length != 0)
System.out.println(a[0]);
}
else
System.out.println("null");
}
void noDanglingElse(Object[] a) {
<warning descr="'if' contains single statement">if</warning> (a != null) {
if (a.length != 0)
System.out.println(a[0]);
else
System.out.println("empty");
}
else
System.out.println("null");
}
void danglingElseNestedIfChain(Object[] a) {
if (a != null) {
if (a.length != 0)
if(a[0] != null)
System.out.println(a[0]);
}
else
System.out.println("null");
}
void danglingElseNestedIfElse(Object[] a) {
if (a != null) {
if (a.length != 0)
if (a[0] != null)
System.out.println(a[0]);
else
System.out.println("missing");
}
else
System.out.println("null");
}
void noDanglingElseNestedIf(Object[] a) {
<warning descr="'if' contains single statement">if</warning> (a != null) {
if (a.length != 0)
if (a[0] != null)
System.out.println(a[0]);
else
System.out.println("missing");
else
System.out.println("empty");
}
else
System.out.println("null");
}
void danglingElseWithLoop(Object[] a) {
if (a != null) {
for (int i = 0; i < a.length; i++)
if (a[i] != null)
System.out.println(a[i]);
}
else
System.out.println("null");
}
void noDanglingElseWithLoop(Object[] a) {
<warning descr="'if' contains single statement">if</warning> (a != null) {
for (int i = 0; i < a.length; i++)
if (a[i] != null)
System.out.println(a[i]);
else
System.out.println("missing");
}
else
System.out.println("null");
}
public int danglingElseWithTwoLoops(Object[] a, Object o) {
if (o == null) {
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
} else
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
return -1;
}
}
@@ -15,13 +15,19 @@
*/
package com.siyeh.ig.style;
import com.siyeh.ig.IGInspectionTestCase;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.siyeh.ig.LightInspectionTestCase;
/**
* @author Pavel.Dolgov
*/
public class SingleStatementInBlockInspectionTest extends IGInspectionTestCase {
public void test() {
doTest("com/siyeh/igtest/style/single_statement_block", new SingleStatementInBlockInspection());
public class SingleStatementInBlockInspectionTest extends LightInspectionTestCase {
public void testSingleStatement() {
doTest();
}
@Override
protected InspectionProfileEntry getInspection() {
return new SingleStatementInBlockInspection();
}
}