IG: Update "Throwable not thrown" inspection for switch expressions (IDEA-203703)

This commit is contained in:
Bas Leijdekkers
2018-12-17 19:30:54 +01:00
parent f602773c6f
commit a3b33f43a9
5 changed files with 107 additions and 66 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2008-2017 Bas Leijdekkers
* Copyright 2008-2018 Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import org.jetbrains.annotations.NotNull;
@@ -121,10 +122,8 @@ public class ThrowableNotThrownInspection extends BaseInspection {
return isIgnored(expression, true);
}
private static boolean isIgnored(PsiElement element, boolean checkDeep) {
final PsiElement parent =
PsiTreeUtil.getParentOfType(element, PsiStatement.class, PsiExpressionList.class, PsiVariable.class,
PsiLambdaExpression.class, PsiPolyadicExpression.class, PsiInstanceOfExpression.class);
private static boolean isIgnored(PsiExpression expression, boolean checkDeep) {
final PsiElement parent = getHandlingParent(expression);
if (parent instanceof PsiVariable) {
if (!(parent instanceof PsiLocalVariable)) {
return false;
@@ -133,45 +132,56 @@ public class ThrowableNotThrownInspection extends BaseInspection {
return checkDeep && !isUsedElsewhere((PsiLocalVariable)parent);
}
}
if (!(parent instanceof PsiStatement)) {
return false;
}
if (parent instanceof PsiReturnStatement || parent instanceof PsiThrowStatement || parent instanceof PsiForeachStatement) {
return false;
}
if (parent instanceof PsiExpressionStatement) {
else if (parent instanceof PsiExpressionStatement) {
// void method (like printStackTrace()) provides no result, thus can't be ignored
final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)parent;
final PsiExpression expression1 = expressionStatement.getExpression();
if (expression1 instanceof PsiMethodCallExpression) {
// void method (like printStackTrace()) provides no result, thus is not ignored
return !PsiType.VOID.equals(expression1.getType());
return !PsiType.VOID.equals(expression1.getType());
}
else if (parent instanceof PsiExpressionList) {
return parent.getParent() instanceof PsiExpressionListStatement;
}
else if (parent instanceof PsiLambdaExpression) {
return PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)parent));
}
else if (parent instanceof PsiReturnStatement || parent instanceof PsiThrowStatement || parent instanceof PsiLoopStatement
|| parent instanceof PsiIfStatement || parent instanceof PsiAssertStatement) {
return false;
}
else if (parent instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
final PsiExpression rhs = assignmentExpression.getRExpression();
if (!PsiTreeUtil.isAncestor(rhs, expression, false)) {
return false;
}
else if (expression1 instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression1;
final PsiExpression rhs = assignmentExpression.getRExpression();
if (!PsiTreeUtil.isAncestor(rhs, element, false)) {
return false;
}
final PsiExpression lhs = ParenthesesUtils.stripParentheses(assignmentExpression.getLExpression());
if (!(lhs instanceof PsiReferenceExpression)) {
return false;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiLocalVariable)) {
return false;
}
return checkDeep && !isUsedElsewhere((PsiLocalVariable)target);
final PsiExpression lhs = ParenthesesUtils.stripParentheses(assignmentExpression.getLExpression());
if (!(lhs instanceof PsiReferenceExpression)) {
return false;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiLocalVariable)) {
return false;
}
return checkDeep && !isUsedElsewhere((PsiLocalVariable)target);
}
return true;
}
private static PsiElement getHandlingParent(PsiExpression expression) {
while (true) {
final PsiElement parent = ExpressionUtils.getPassThroughParent(expression);
if (!(parent instanceof PsiExpression) || parent instanceof PsiLambdaExpression || parent instanceof PsiAssignmentExpression) {
return parent;
}
expression = (PsiExpression)parent;
}
}
private static boolean isUsedElsewhere(PsiLocalVariable variable) {
final Query<PsiReference> query = ReferencesSearch.search(variable);
for (PsiReference reference : query) {
final PsiElement usage = reference.getElement();
if (!isIgnored(usage, false)) {
if (reference instanceof PsiReferenceExpression && !isIgnored((PsiExpression)reference, false)) {
return true;
}
}
@@ -1420,4 +1420,36 @@ public class ExpressionUtils {
}
}
}
public static PsiElement getPassThroughParent(PsiExpression expression) {
while (true) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiParenthesizedExpression || parent instanceof PsiTypeCastExpression) {
expression = (PsiExpression)parent;
continue;
}
else if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != expression) {
expression = (PsiExpression)parent;
continue;
}
else if (parent instanceof PsiExpressionStatement) {
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiSwitchLabeledRuleStatement) {
final PsiSwitchBlock block = ((PsiSwitchLabeledRuleStatement)grandParent).getEnclosingSwitchBlock();
if (block instanceof PsiSwitchExpression) {
expression = (PsiExpression)block;
continue;
}
}
}
else if (parent instanceof PsiBreakStatement) {
final PsiElement exitedElement = ((PsiBreakStatement)parent).findExitedElement();
if (exitedElement instanceof PsiSwitchExpression) {
expression = (PsiExpression)exitedElement;
continue;
}
}
return parent;
}
}
}
@@ -322,7 +322,7 @@ public abstract class ResourceInspection extends BaseInspection {
}
}
}
final PsiElement parent = getPassthroughParent(resourceCreationExpression);
final PsiElement parent = ExpressionUtils.getPassThroughParent(resourceCreationExpression);
if (parent instanceof PsiReturnStatement) {
return true;
}
@@ -361,38 +361,6 @@ public abstract class ResourceInspection extends BaseInspection {
return visitor.isEscaped();
}
private static PsiElement getPassthroughParent(PsiExpression expression) {
while (true) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiParenthesizedExpression || parent instanceof PsiTypeCastExpression) {
expression = (PsiExpression)parent;
continue;
}
else if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != expression) {
expression = (PsiExpression)parent;
continue;
}
else if (parent instanceof PsiExpressionStatement) {
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiSwitchLabeledRuleStatement) {
final PsiSwitchBlock block = ((PsiSwitchLabeledRuleStatement)grandParent).getEnclosingSwitchBlock();
if (block instanceof PsiSwitchExpression) {
expression = (PsiExpression)block;
continue;
}
}
}
else if (parent instanceof PsiBreakStatement) {
final PsiElement exitedElement = ((PsiBreakStatement)parent).findExitedElement();
if (exitedElement instanceof PsiSwitchExpression) {
expression = (PsiExpression)exitedElement;
continue;
}
}
return parent;
}
}
private class CloseVisitor extends JavaRecursiveElementWalkingVisitor {
private boolean containsClose;
private final PsiVariable resource;
@@ -106,4 +106,27 @@ class L {
final RuntimeException exception = new RuntimeException();
I i2 = () -> exception;
}
}
class Main {
Runnable r = () -> <warning descr="Runtime exception instance 'new RuntimeException()' is not thrown">new RuntimeException()</warning>;
Throwable switchExpression1(int i) {
return switch(i) {
default -> new Throwable();
};
}
Throwable switchExpression2(int i) {
for (int j = 0; j < 10; j++, <warning descr="Throwable instance 'new Throwable()' is not thrown">new Throwable()</warning>) {}
return switch(i) {
default -> {
break new Throwable();
}
};
}
void x(I i) {
assert i.get() != null;
}
}
@@ -1,7 +1,9 @@
package com.siyeh.ig.bugs;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.testFramework.LightProjectDescriptor;
import com.siyeh.ig.LightInspectionTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ThrowableInstanceNeverThrownInspectionTest extends LightInspectionTestCase {
@@ -16,6 +18,12 @@ public class ThrowableInstanceNeverThrownInspectionTest extends LightInspectionT
return new ThrowableNotThrownInspection();
}
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_12;
}
@Override
protected String getBasePath() {
return "/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/throwable_instance_never_thrown";