mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
lambda: check unhandled exceptions for method references; disable surround with try/catch accordingly (IDEA-98966)
This commit is contained in:
@@ -1022,6 +1022,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!myHolder.hasErrorResults()) {
|
||||
myHolder.add(HighlightUtil.checkUnhandledExceptions(expression, expression.getTextRange()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,10 +38,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class SurroundWithTryCatchFix implements IntentionAction {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.SurroundWithTryCatchFix");
|
||||
|
||||
private PsiStatement myStatement;
|
||||
private PsiStatement myStatement = null;
|
||||
|
||||
public SurroundWithTryCatchFix(PsiElement element) {
|
||||
myStatement = PsiTreeUtil.getNonStrictParentOfType(element, PsiStatement.class);
|
||||
final PsiMethodReferenceExpression methodReferenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class, false);
|
||||
if (methodReferenceExpression == null) {
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class);
|
||||
if (lambdaExpression == null || lambdaExpression.getBody() instanceof PsiCodeBlock) {
|
||||
myStatement = PsiTreeUtil.getNonStrictParentOfType(element, PsiStatement.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -240,6 +240,9 @@ public class ExceptionUtil {
|
||||
PsiCallExpression expression = (PsiCallExpression)element;
|
||||
unhandledExceptions = getUnhandledExceptions(expression, topElement, includeSelfCalls);
|
||||
}
|
||||
else if (element instanceof PsiMethodReferenceExpression) {
|
||||
unhandledExceptions = getUnhandledExceptions((PsiMethodReferenceExpression)element, topElement);
|
||||
}
|
||||
else if (element instanceof PsiThrowStatement) {
|
||||
PsiThrowStatement statement = (PsiThrowStatement)element;
|
||||
unhandledExceptions = getUnhandledExceptions(statement, topElement);
|
||||
@@ -310,6 +313,16 @@ public class ExceptionUtil {
|
||||
return foundExceptions;
|
||||
}
|
||||
|
||||
private static Collection<PsiClassType> getUnhandledExceptions(PsiMethodReferenceExpression methodReferenceExpression,
|
||||
PsiElement topElement) {
|
||||
final JavaResolveResult resolveResult = methodReferenceExpression.advancedResolve(false);
|
||||
final PsiElement resolve = resolveResult.getElement();
|
||||
if (resolve instanceof PsiMethod) {
|
||||
return getUnhandledExceptions((PsiMethod)resolve, methodReferenceExpression, topElement, resolveResult.getSubstitutor());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static boolean firstStatementIsConstructorCall(PsiCodeBlock constructorBody) {
|
||||
final PsiStatement[] statements = constructorBody.getStatements();
|
||||
if (statements.length == 0) return false;
|
||||
@@ -337,6 +350,12 @@ public class ExceptionUtil {
|
||||
visitElement(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
|
||||
addExceptions(array, getUnhandledExceptions(expression, null));
|
||||
visitElement(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitResourceVariable(PsiResourceVariable resourceVariable) {
|
||||
addExceptions(array, getUnhandledCloserExceptions(resourceVariable, null));
|
||||
@@ -472,12 +491,17 @@ public class ExceptionUtil {
|
||||
}
|
||||
|
||||
private static boolean isArrayClone(PsiMethod method, PsiElement element) {
|
||||
if (!(element instanceof PsiMethodCallExpression)) return false;
|
||||
if (!method.getName().equals(CLONE_METHOD_NAME)) return false;
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass == null || !CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
|
||||
return false;
|
||||
}
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
final PsiMethodReferenceExpression methodCallExpression = (PsiMethodReferenceExpression)element;
|
||||
final PsiExpression qualifierExpression = methodCallExpression.getQualifierExpression();
|
||||
return qualifierExpression != null && qualifierExpression.getType() instanceof PsiArrayType;
|
||||
}
|
||||
if (!(element instanceof PsiMethodCallExpression)) return false;
|
||||
|
||||
PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
|
||||
final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
|
||||
@@ -545,7 +569,12 @@ public class ExceptionUtil {
|
||||
return parent instanceof PsiAnonymousClass && isHandled(parent, exceptionType, topElement);
|
||||
}
|
||||
else if (parent instanceof PsiLambdaExpression) {
|
||||
return true;
|
||||
final PsiType interfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType();
|
||||
return isDeclaredBySAMMethod(exceptionType, interfaceType);
|
||||
}
|
||||
else if (element instanceof PsiMethodReferenceExpression) {
|
||||
final PsiType interfaceType = ((PsiMethodReferenceExpression)element).getFunctionalInterfaceType();
|
||||
return isDeclaredBySAMMethod(exceptionType, interfaceType);
|
||||
}
|
||||
else if (parent instanceof PsiClassInitializer) {
|
||||
if (((PsiClassInitializer)parent).hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
@@ -592,6 +621,16 @@ public class ExceptionUtil {
|
||||
return isHandled(parent, exceptionType, topElement);
|
||||
}
|
||||
|
||||
private static boolean isDeclaredBySAMMethod(PsiClassType exceptionType, PsiType interfaceType) {
|
||||
if (interfaceType != null) {
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(interfaceType);
|
||||
if (interfaceMethod != null) {
|
||||
return isHandledByMethodThrowsClause(interfaceMethod, exceptionType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean areAllConstructorsThrow(final PsiClass aClass, PsiClassType exceptionType) {
|
||||
if (aClass == null) return false;
|
||||
final PsiMethod[] constructors = aClass.getConstructors();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = (t) -> <error descr="Unhandled exception: ExTest.Ex">ExTest.maybeThrow(t)</error>;
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = <error descr="Unhandled exception: ExTest.Ex">ExTest::maybeThrow</error>;
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// "Surround with try/catch" "true"
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = (t) -> {
|
||||
try {
|
||||
return ExTest.maybeThrow(t);
|
||||
} catch (Ex ex) {
|
||||
<selection>ex.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.</selection>
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Surround with try/catch" "true"
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = (t) -> { return ExTest.may<caret>beThrow(t);};
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Surround with try/catch" "false"
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = (t) -> ExTest.may<caret>beThrow(t);
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Surround with try/catch" "false"
|
||||
public class ExTest {
|
||||
public static void maybeThrow(String data) throws Ex {
|
||||
throw new Ex(data);
|
||||
}
|
||||
|
||||
{
|
||||
Block<String> b = ExTest::may<caret>beThrow(t);
|
||||
}
|
||||
|
||||
|
||||
private static class Ex extends Throwable {
|
||||
public Ex(String s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Block<T> {
|
||||
public void accept(T t);
|
||||
}
|
||||
@@ -150,6 +150,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testUnhandledExceptions() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testReturnValue() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@@ -157,6 +157,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testUnhandledExceptions() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user