diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index fe68c1c00278..464abd6f8ca5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -16,6 +16,7 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; @@ -1041,9 +1042,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { ((PsiResourceExpression)resource).getExpression().accept(this); } - PsiMethod closer = PsiUtil.getResourceCloserMethod(resource); - if (closer != null) { - addMethodThrows(closer, null); + final List closerExceptions = ExceptionUtil.getCloserExceptions(resource); + if (!closerExceptions.isEmpty()) { + addThrows(null, findNextCatch(false), closerExceptions.toArray(new PsiClassType[closerExceptions.size()])); } } } @@ -1411,15 +1412,19 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { CatchDescriptor cd = findNextCatch(false); if (method != null) { PsiClassType[] refs = method.getThrowsList().getReferencedTypes(); - for (PsiClassType ref : refs) { - pushUnknown(); - ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null); - addInstruction(cond); - addInstruction(new EmptyStackInstruction()); - initException(ref, cd); - addThrowCode(cd, explicitCall); - cond.setOffset(myCurrentFlow.getInstructionCount()); - } + addThrows(explicitCall, cd, refs); + } + } + + private void addThrows(@Nullable PsiElement explicitCall, CatchDescriptor cd, PsiClassType[] refs) { + for (PsiClassType ref : refs) { + pushUnknown(); + ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null); + addInstruction(cond); + addInstruction(new EmptyStackInstruction()); + initException(ref, cd); + addThrowCode(cd, explicitCall); + cond.setOffset(myCurrentFlow.getInstructionCount()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java index 3b2ffe5a6832..e6b2e95de45a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java @@ -100,11 +100,6 @@ public class TryWithResourcesPostfixTemplate extends PostfixTemplate { @NotNull private static Collection getUnhandled(@NotNull PsiExpression expression) { assert expression.getType() != null; - PsiMethod methodCloser = PsiUtil.getResourceCloserMethodForType((PsiClassType)expression.getType()); - PsiSubstitutor substitutor = PsiUtil.resolveGenericsClassInType(expression.getType()).getSubstitutor(); - - return methodCloser != null - ? ExceptionUtil.getUnhandledExceptions(methodCloser, expression, null, substitutor) - : Collections.emptyList(); + return ExceptionUtil.getUnhandledCloserExceptions(expression, null, expression.getType()); } } diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 5360eb550f29..289154d7e930 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -1137,20 +1137,9 @@ public final class PsiUtil extends PsiUtilCore { return name != null && IGNORED_NAMES.contains(name); } - @Nullable - public static PsiMethod getResourceCloserMethod(@NotNull PsiResourceListElement resource) { - PsiType resourceType = resource.getType(); - return resourceType instanceof PsiClassType ? getResourceCloserMethodForType((PsiClassType)resourceType) : null; - } - - /** @deprecated use {@link #getResourceCloserMethod(PsiResourceListElement)} (to be removed in IDEA 17) */ - @SuppressWarnings("unused") - public static PsiMethod getResourceCloserMethod(@NotNull PsiResourceVariable resource) { - return getResourceCloserMethod((PsiResourceListElement)resource); - } @Nullable - public static PsiMethod getResourceCloserMethodForType(@NotNull final PsiClassType resourceType) { + public static PsiMethod[] getResourceCloserMethodsForType(@NotNull final PsiClassType resourceType) { final PsiClass resourceClass = resourceType.resolve(); if (resourceClass == null) return null; @@ -1162,7 +1151,10 @@ public final class PsiUtil extends PsiUtilCore { if (JavaClassSupers.getInstance().getSuperClassSubstitutor(autoCloseable, resourceClass, resourceType.getResolveScope(), PsiSubstitutor.EMPTY) == null) return null; final PsiMethod[] closes = autoCloseable.findMethodsByName("close", false); - return closes.length == 1 ? resourceClass.findMethodBySignature(closes[0], true) : null; + if (closes.length == 1) { + return resourceClass.findMethodsBySignature(closes[0], true); + } + return null; } @Nullable diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java index 7f9c6cc8194c..fc9083936d24 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -38,8 +38,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.intellij.openapi.util.Pair.pair; - /** * @author mike */ @@ -533,38 +531,66 @@ public class ExceptionUtil { if (psiType instanceof PsiClassType) { ex.add((PsiClassType)psiType); } + else if (psiType instanceof PsiCapturedWildcardType) { + final PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)psiType; + final PsiType upperBound = capturedWildcardType.getUpperBound(); + if (upperBound instanceof PsiClassType) { + ex.add((PsiClassType)upperBound); + } + } } return ex; } @NotNull public static List getCloserExceptions(@NotNull PsiResourceListElement resource) { - Pair closer = resolveCloser(resource); - return closer != null ? getExceptionsByMethod(closer.first, closer.second, resource) : Collections.emptyList(); + List ex = getExceptionsFromClose(resource); + return ex != null ? ex : Collections.emptyList(); } @NotNull public static List getUnhandledCloserExceptions(@NotNull PsiResourceListElement resource, @Nullable PsiElement topElement) { - Pair closer = resolveCloser(resource); - return closer != null ? getUnhandledExceptions(closer.first, resource, topElement, closer.second) : Collections.emptyList(); + final PsiType type = resource.getType(); + return getUnhandledCloserExceptions(resource, topElement, type); } - private static Pair resolveCloser(PsiResourceListElement resource) { - PsiMethod method = PsiUtil.getResourceCloserMethod(resource); - if (method != null) { - PsiClass closerClass = method.getContainingClass(); - if (closerClass != null) { - PsiClassType.ClassResolveResult resourceType = PsiUtil.resolveGenericsClassInType(resource.getType()); - if (resourceType != null) { - PsiClass resourceClass = resourceType.getElement(); - if (resourceClass != null) { - PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(closerClass, resourceClass, resourceType.getSubstitutor()); - if (substitutor != null) { - return pair(method, substitutor); + @NotNull + public static List getUnhandledCloserExceptions(PsiElement place, @Nullable PsiElement topElement, PsiType type) { + List ex = type instanceof PsiClassType ? getExceptionsFromClose(type, place.getResolveScope()) : null; + return ex != null ? getUnhandledExceptions(place, topElement, PsiSubstitutor.EMPTY, ex.toArray(new PsiClassType[ex.size()])) : Collections.emptyList(); + } + + private static List getExceptionsFromClose(PsiResourceListElement resource) { + final PsiType type = resource.getType(); + return type instanceof PsiClassType ? getExceptionsFromClose(type, resource.getResolveScope()) : null; + } + + private static List getExceptionsFromClose(PsiType type, GlobalSearchScope scope) { + PsiClassType.ClassResolveResult resourceType = PsiUtil.resolveGenericsClassInType(type); + PsiClass resourceClass = resourceType.getElement(); + if (resourceClass == null) return null; + + PsiMethod[] methods = PsiUtil.getResourceCloserMethodsForType((PsiClassType)type); + if (methods != null) { + List ex = null; + for (PsiMethod method : methods) { + PsiClass closerClass = method.getContainingClass(); + if (closerClass != null) { + PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(closerClass, resourceClass, resourceType.getSubstitutor()); + if (substitutor != null) { + final PsiClassType[] exceptionTypes = method.getThrowsList().getReferencedTypes(); + if (exceptionTypes.length == 0) return Collections.emptyList(); + + if (ex == null) { + ex = collectSubstituted(substitutor, exceptionTypes, scope); + } + else { + retainExceptions(ex, collectSubstituted(substitutor, exceptionTypes, scope)); } } } } + return ex; } return null; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResourcesWithMultipleCloseInterfaces.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResourcesWithMultipleCloseInterfaces.java new file mode 100644 index 000000000000..2ff36864df68 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResourcesWithMultipleCloseInterfaces.java @@ -0,0 +1,48 @@ +import java.io.IOException; + +class Ex extends Exception {} + +interface IOCloseable { + void close() throws IOException; +} + +interface ExCloseable { + void close() throws Ex; +} + +interface NoExCloseable { + void close(); +} + +interface TCloseable { + void close() throws T; +} + +interface I1 extends AutoCloseable, IOCloseable {} +interface I1R extends IOCloseable, AutoCloseable {} +interface I2 extends AutoCloseable, IOCloseable, ExCloseable {} +interface I3 extends AutoCloseable, NoExCloseable {} +interface I3R extends NoExCloseable, AutoCloseable {} + +interface IT extends AutoCloseable, TCloseable {} + +class Main { + { + try (I1 i1 = null) {} + try (I1 i11 = null) { + } catch (IOException e){} + try (I1R i11r = null) { + } catch (IOException e){} + + try (I2 i2 = null) {} + try (I2 i21 = null) { + } catch (IOException e) {} + try (I2 i22 = null) { + } catch (Ex e) {} + + try (I3 i3 = null) {} + try (I3R i3r = null) {} + + try (IT it = null) {} + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java index c7611febe7f0..9f94acbbdd8e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk7Test.java @@ -180,6 +180,7 @@ public class LightAdvHighlightingJdk7Test extends LightDaemonAnalyzerTestCase { public void testUncheckedExtendedWarnings() { doTest(true, false); } public void testInaccessibleInferredTypeForVarargsArgument() { doTest(false, false);} public void testRuntimeClassCast() { doTest(true, false);} + public void testTryWithResourcesWithMultipleCloseInterfaces() { doTest(false, false);} public void testJavaUtilCollections_NoVerify() throws Exception { PsiClass collectionsClass = getJavaFacade().findClass("java.util.Collections", GlobalSearchScope.moduleWithLibrariesScope(getModule())); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java index 4bd074c13634..a4a392190344 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExceptionUtils.java @@ -15,8 +15,8 @@ */ package com.siyeh.ig.psiutils; +import com.intellij.codeInsight.ExceptionUtil; import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,8 +48,7 @@ public class ExceptionUtils { if (element instanceof PsiResourceList) { final PsiResourceList resourceList = (PsiResourceList)element; for (PsiResourceListElement resource : resourceList) { - final PsiMethod method = PsiUtil.getResourceCloserMethod(resource); - collectExceptionsThrown(method, PsiSubstitutor.EMPTY, out); + out.addAll(ExceptionUtil.getCloserExceptions(resource)); } } final ExceptionsThrownVisitor visitor = new ExceptionsThrownVisitor(out); @@ -202,26 +201,6 @@ public class ExceptionUtils { return false; } - private static void collectExceptionsThrown(@Nullable PsiMethod method, @NotNull PsiSubstitutor substitutor, - @NotNull Set out) { - if (method == null) { - return; - } - for (PsiClassType type : method.getThrowsList().getReferencedTypes()) { - final PsiType substitute = substitutor.substitute(type); - if (substitute instanceof PsiClassType) { - out.add((PsiClassType)substitute); - } - else if (substitute instanceof PsiCapturedWildcardType) { - final PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)substitute; - final PsiType upperBound = capturedWildcardType.getUpperBound(); - if (upperBound instanceof PsiClassType) { - out.add((PsiClassType)upperBound); - } - } - } - } - public static Set getExceptionTypesHandled(PsiTryStatement statement) { final Set out = new HashSet<>(5); for (PsiParameter parameter : statement.getCatchBlockParameters()) { @@ -258,7 +237,8 @@ public class ExceptionUtils { if (!(target instanceof PsiMethod)) { return; } - collectExceptionsThrown((PsiMethod)target, resolveResult.getSubstitutor(), m_exceptionsThrown); + final PsiClassType[] referencedTypes = ((PsiMethod)target).getThrowsList().getReferencedTypes(); + m_exceptionsThrown.addAll(ExceptionUtil.collectSubstituted(resolveResult.getSubstitutor(), referencedTypes, callExpression.getResolveScope())); } @Override