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 0dea69d836d0..6346218387d8 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 @@ -16,6 +16,7 @@ package com.intellij.psi.util; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; @@ -29,6 +30,7 @@ import com.intellij.psi.infos.MethodCandidateInfo.ApplicabilityLevel; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.meta.PsiMetaData; import com.intellij.psi.meta.PsiMetaOwner; +import com.intellij.psi.search.ProjectScope; import com.intellij.psi.tree.IElementType; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; @@ -964,4 +966,22 @@ public final class PsiUtil extends PsiUtilCore { public static boolean isIgnoredName(@Nullable final String name) { return "ignore".equals(name) || "ignored".equals(name); } + + @Nullable + public static PsiMethod getResourceCloserMethod(@NotNull final PsiResourceVariable resource) { + final PsiType resourceType = resource.getType(); + if (!(resourceType instanceof PsiClassType)) return null; + final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); + if (resourceClass == null) return null; + + final Project project = resource.getProject(); + final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); + if (autoCloseable == null) return null; + + if (!InheritanceUtil.isInheritorOrSelf(resourceClass, autoCloseable, true)) return null; + + final PsiMethod[] closes = autoCloseable.findMethodsByName("close", false); + return closes.length == 1 ? resourceClass.findMethodBySignature(closes[0], true) : null; + } } 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 ed67e417757c..3c90c2429ce7 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -16,7 +16,6 @@ package com.intellij.codeInsight; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.NullableComputable; import com.intellij.psi.*; @@ -24,7 +23,10 @@ import com.intellij.psi.controlFlow.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectScope; -import com.intellij.psi.util.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; @@ -44,7 +46,7 @@ public class ExceptionUtil { @NotNull public static List getThrownExceptions(@NotNull PsiElement[] elements) { - List array = new ArrayList(); + List array = ContainerUtil.newArrayList(); for (PsiElement element : elements) { List exceptions = getThrownExceptions(element); addExceptions(array, exceptions); @@ -63,7 +65,7 @@ public class ExceptionUtil { @NotNull private static List filterOutUncheckedExceptions(List exceptions) { - List array = new ArrayList(); + List array = ContainerUtil.newArrayList(); for (PsiClassType exception : exceptions) { if (!isUncheckedException(exception)) array.add(exception); } @@ -104,11 +106,18 @@ public class ExceptionUtil { else if (element instanceof PsiTryStatement) { return getTryExceptions((PsiTryStatement)element); } + else if (element instanceof PsiResourceVariable) { + final PsiResourceVariable variable = (PsiResourceVariable)element; + final List types = getCloserExceptions(variable); + final PsiExpression initializer = variable.getInitializer(); + if (initializer != null) addExceptions(types, getThrownExceptions(initializer)); + return types; + } return getThrownExceptions(element.getChildren()); } private static List getTryExceptions(PsiTryStatement tryStatement) { - List array = new ArrayList(); + List array = ContainerUtil.newArrayList(); PsiResourceList resourceList = tryStatement.getResourceList(); if (resourceList != null) { @@ -162,23 +171,30 @@ public class ExceptionUtil { @NotNull private static List getExceptionsByMethodAndChildren(PsiElement element, JavaResolveResult resolveResult) { + List result = ContainerUtil.newArrayList(); + PsiMethod method = (PsiMethod)resolveResult.getElement(); - List result = new ArrayList(); if (method != null) { - PsiSubstitutor substitutor = resolveResult.getSubstitutor(); - final PsiClassType[] referenceTypes = method.getThrowsList().getReferencedTypes(); - for (PsiType type : referenceTypes) { - type = substitutor.substitute(type); - if (type instanceof PsiClassType) { - result.add((PsiClassType)type); - } + addExceptions(result, getExceptionsByMethod(method, resolveResult.getSubstitutor())); + } + + addExceptions(result, getThrownExceptions(element.getChildren())); + + return result; + } + + @NotNull + private static List getExceptionsByMethod(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) { + List result = ContainerUtil.newArrayList(); + + PsiClassType[] referenceTypes = method.getThrowsList().getReferencedTypes(); + for (PsiType type : referenceTypes) { + type = substitutor.substitute(type); + if (type instanceof PsiClassType) { + result.add((PsiClassType)type); } } - PsiElement[] children = element.getChildren(); - for (PsiElement child : children) { - addExceptions(result, getThrownExceptions(child)); - } return result; } @@ -201,22 +217,23 @@ public class ExceptionUtil { } @NotNull - public static Collection collectUnhandledExceptions(@NotNull PsiElement element, - @Nullable PsiElement topElement) { + public static Collection collectUnhandledExceptions(@NotNull PsiElement element, @Nullable PsiElement topElement) { return collectUnhandledExceptions(element, topElement, true); } @NotNull public static Collection collectUnhandledExceptions(@NotNull PsiElement element, - @Nullable PsiElement topElement, boolean includeSelfCalls) { + @Nullable PsiElement topElement, + boolean includeSelfCalls) { final Set set = collectUnhandledExceptions(element, topElement, null, includeSelfCalls); return set == null ? Collections.emptyList() : set; } @Nullable private static Set collectUnhandledExceptions(@NotNull PsiElement element, - PsiElement topElement, - @Nullable Set foundExceptions, boolean includeSelfCalls) { + @Nullable PsiElement topElement, + @Nullable Set foundExceptions, + boolean includeSelfCalls) { Collection unhandledExceptions = null; if (element instanceof PsiCallExpression) { PsiCallExpression expression = (PsiCallExpression)element; @@ -303,7 +320,7 @@ public class ExceptionUtil { @NotNull public static List getUnhandledExceptions(PsiElement[] elements) { - final List array = new ArrayList(); + final List array = ContainerUtil.newArrayList(); final PsiElementVisitor visitor = new JavaRecursiveElementWalkingVisitor() { @Override public void visitCallExpression(PsiCallExpression expression) { @@ -373,28 +390,15 @@ public class ExceptionUtil { } @NotNull - public static List getUnhandledCloserExceptions(final PsiResourceVariable resource, @Nullable final PsiElement topElement) { - final Project project = resource.getProject(); - final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); - if (autoCloseable != null) { - final PsiMethod[] methods = autoCloseable.findMethodsByName("close", false); - if (methods.length == 1) { - final MethodSignature signature = methods[0].getSignature(PsiSubstitutor.EMPTY); - final PsiType resourceType = resource.getType(); - if (resourceType instanceof PsiClassType) { - final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); - if (resourceClass != null) { - final PsiMethod method = MethodSignatureUtil.findMethodBySignature(resourceClass, signature, true); - if (method != null) { - return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY); - } - } - } - } - } + public static List getCloserExceptions(final PsiResourceVariable resource) { + final PsiMethod method = PsiUtil.getResourceCloserMethod(resource); + return method != null ? getExceptionsByMethod(method, PsiSubstitutor.EMPTY) : Collections.emptyList(); + } - return Collections.emptyList(); + @NotNull + public static List getUnhandledCloserExceptions(final PsiResourceVariable resource, @Nullable final PsiElement topElement) { + final PsiMethod method = PsiUtil.getResourceCloserMethod(resource); + return method != null ? getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY) : Collections.emptyList(); } @NotNull @@ -444,7 +448,7 @@ public class ExceptionUtil { } final PsiClassType[] referencedTypes = method.getThrowsList().getReferencedTypes(); if (referencedTypes.length > 0) { - List result = new ArrayList(); + List result = ContainerUtil.newArrayList(); for (PsiClassType referencedType : referencedTypes) { final PsiType type = substitutor.substitute(referencedType); @@ -477,20 +481,20 @@ public class ExceptionUtil { return qualifierExpression != null && qualifierExpression.getType() instanceof PsiArrayType; } - public static boolean isUncheckedException(PsiClassType type) { - final GlobalSearchScope searchScope = type.getResolveScope(); + public static boolean isUncheckedException(@NotNull PsiClassType type) { final PsiClass aClass = type.resolve(); if (aClass == null) return false; + + final GlobalSearchScope searchScope = ProjectScope.getLibrariesScope(aClass.getProject()); final PsiClass runtimeExceptionClass = ApplicationManager.getApplication().runReadAction( new NullableComputable() { @Override public PsiClass compute() { - return JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.RuntimeException", searchScope); + return JavaPsiFacade.getInstance(aClass.getProject()).findClass(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, searchScope); } } ); - if (runtimeExceptionClass != null && - InheritanceUtil.isInheritorOrSelf(aClass, runtimeExceptionClass, true)) { + if (runtimeExceptionClass != null && InheritanceUtil.isInheritorOrSelf(aClass, runtimeExceptionClass, true)) { return true; } @@ -498,11 +502,15 @@ public class ExceptionUtil { new NullableComputable() { @Override public PsiClass compute() { - return JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Error", searchScope); + return JavaPsiFacade.getInstance(aClass.getProject()).findClass(CommonClassNames.JAVA_LANG_ERROR, searchScope); } } ); - return errorClass != null && InheritanceUtil.isInheritorOrSelf(aClass, errorClass, true); + if (errorClass != null && InheritanceUtil.isInheritorOrSelf(aClass, errorClass, true)) { + return true; + } + + return false; } public static boolean isUncheckedExceptionOrSuperclass(@NotNull final PsiClassType type) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiCatchSectionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiCatchSectionImpl.java index 31dfab53144c..66af9f7f8eb5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiCatchSectionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiCatchSectionImpl.java @@ -33,7 +33,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -109,41 +108,46 @@ public class PsiCatchSectionImpl extends CompositePsiElement implements PsiCatch // When the thrown expression is an ... exception parameter Ej (parameter) of a catch clause Cj (this) ... if (PsiUtil.getLanguageLevel(parameter).isAtLeast(LanguageLevel.JDK_1_7) && isCatchParameterEffectivelyFinal(parameter, getCatchBlock())) { + // ... and the try block of the try statement which declares Cj (tryBlock) can throw T ... + final List thrownTypes = ContainerUtil.newArrayList(); final PsiCodeBlock tryBlock = getTryStatement().getTryBlock(); if (tryBlock != null) { - // ... and the try block of the try statement which declares Cj (tryBlock) can throw T ... - final List thrownTypes = ExceptionUtil.getThrownExceptions(tryBlock); - if (!thrownTypes.isEmpty()) { - // ... and for all exception parameters Ei declared by any catch clauses Ci, 1 <= i < j, - // declared to the left of Cj for the same try statement, T is not assignable to Ei ... - final PsiParameter[] parameters = getTryStatement().getCatchBlockParameters(); - final List uncaughtTypes = ContainerUtil.mapNotNull(thrownTypes, new NullableFunction() { - @Override - public PsiType fun(final PsiClassType thrownType) { - for (int i = 0; i < parameters.length && parameters[i] != parameter; i++) { - final PsiType catchType = parameters[i].getType(); - if (catchType.isAssignableFrom(thrownType)) return null; - } - return thrownType; + thrownTypes.addAll(ExceptionUtil.getThrownExceptions(tryBlock)); + } + final PsiResourceList resourceList = getTryStatement().getResourceList(); + if (resourceList != null) { + thrownTypes.addAll(ExceptionUtil.getThrownExceptions(resourceList)); + } + if (!thrownTypes.isEmpty()) { + // ... and for all exception parameters Ei declared by any catch clauses Ci, 1 <= i < j, + // declared to the left of Cj for the same try statement, T is not assignable to Ei ... + final PsiParameter[] parameters = getTryStatement().getCatchBlockParameters(); + final List uncaughtTypes = ContainerUtil.mapNotNull(thrownTypes, new NullableFunction() { + @Override + public PsiType fun(final PsiClassType thrownType) { + for (int i = 0; i < parameters.length && parameters[i] != parameter; i++) { + final PsiType catchType = parameters[i].getType(); + if (catchType.isAssignableFrom(thrownType)) return null; } - }); - // ... and T is assignable to Ej ... - if (!uncaughtTypes.isEmpty()) { - boolean passed = true; - for (PsiType type : uncaughtTypes) { - if (!declaredType.isAssignableFrom(type)) { - passed = false; - break; - } - } - // ... the throw statement throws precisely the set of exception types T. - if (passed) return uncaughtTypes; + return thrownType; } + }); + // ... and T is assignable to Ej ... + if (!uncaughtTypes.isEmpty()) { + boolean passed = true; + for (PsiType type : uncaughtTypes) { + if (!declaredType.isAssignableFrom(type)) { + passed = false; + break; + } + } + // ... the throw statement throws precisely the set of exception types T. + if (passed) return uncaughtTypes; } } } - return Arrays.asList(declaredType); + return Collections.singletonList(declaredType); } // do not use control flow here to avoid dead loop diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PreciseRethrow.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PreciseRethrow.java index 4ec5904cfdd4..4c3c3edc75b3 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PreciseRethrow.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/PreciseRethrow.java @@ -156,7 +156,7 @@ class C { } } - void m11() throws E1 { + void m11() { try { System.out.println(); } @@ -165,4 +165,22 @@ class C { throw e; } } + + static class MyResource implements AutoCloseable { + public void close() throws E1 { } + } + + MyResource getResource() throws E2 { + return null; + } + + void m12() { + try (MyResource r = getResource()) { + System.out.println(r); + } + catch (Exception e) { + // test for another precise types calculation fix + throw e; + } + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ExceptionUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ExceptionUtils.java index a2e0da81e727..e99fdf98b9d0 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ExceptionUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ExceptionUtils.java @@ -15,11 +15,8 @@ */ package com.siyeh.ig.psiutils; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.search.ProjectScope; -import com.intellij.psi.util.InheritanceUtil; -import com.siyeh.HardcodedMethodConstants; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -217,11 +214,7 @@ public class ExceptionUtils { final List resourceVariables = resourceList.getResourceVariables(); for (PsiResourceVariable resourceVariable : resourceVariables) { final Set resourceExceptions = calculateExceptionsThrown(resourceVariable); - final PsiType type = resourceVariable.getType(); - if (type instanceof PsiClassType) { - final PsiClassType classType = (PsiClassType)type; - collectExceptionsThrown(findAutoCloseableCloseMethod(classType.resolve()), resourceExceptions); - } + collectExceptionsThrown(PsiUtil.getResourceCloserMethod(resourceVariable), resourceExceptions); for (PsiClassType resourceException : resourceExceptions) { if (!isExceptionHandled(exceptionsHandled, resourceException)) { m_exceptionsThrown.add(resourceException); @@ -265,21 +258,6 @@ public class ExceptionUtils { } } - @Nullable - private static PsiMethod findAutoCloseableCloseMethod(@Nullable PsiClass aClass) { - if (aClass == null || !InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE)) { - return null; - } - final Project project = aClass.getProject(); - final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); - if (autoCloseable == null) { - return null; - } - final PsiMethod closeMethod = autoCloseable.findMethodsByName(HardcodedMethodConstants.CLOSE, false)[0]; - return aClass.findMethodBySignature(closeMethod, true); - } - private static boolean isExceptionHandled(Iterable exceptionsHandled, PsiType thrownType) { for (PsiType exceptionHandled : exceptionsHandled) { if (exceptionHandled.isAssignableFrom(thrownType)) {