Fix precise type calculation for try-with-resources

This commit is contained in:
Roman Shevchenko
2012-10-30 17:01:05 +01:00
parent b9e861fc47
commit 7111f4ff4f
5 changed files with 133 additions and 105 deletions
@@ -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;
}
}
@@ -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<PsiClassType> getThrownExceptions(@NotNull PsiElement[] elements) {
List<PsiClassType> array = new ArrayList<PsiClassType>();
List<PsiClassType> array = ContainerUtil.newArrayList();
for (PsiElement element : elements) {
List<PsiClassType> exceptions = getThrownExceptions(element);
addExceptions(array, exceptions);
@@ -63,7 +65,7 @@ public class ExceptionUtil {
@NotNull
private static List<PsiClassType> filterOutUncheckedExceptions(List<PsiClassType> exceptions) {
List<PsiClassType> array = new ArrayList<PsiClassType>();
List<PsiClassType> 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<PsiClassType> types = getCloserExceptions(variable);
final PsiExpression initializer = variable.getInitializer();
if (initializer != null) addExceptions(types, getThrownExceptions(initializer));
return types;
}
return getThrownExceptions(element.getChildren());
}
private static List<PsiClassType> getTryExceptions(PsiTryStatement tryStatement) {
List<PsiClassType> array = new ArrayList<PsiClassType>();
List<PsiClassType> array = ContainerUtil.newArrayList();
PsiResourceList resourceList = tryStatement.getResourceList();
if (resourceList != null) {
@@ -162,23 +171,30 @@ public class ExceptionUtil {
@NotNull
private static List<PsiClassType> getExceptionsByMethodAndChildren(PsiElement element, JavaResolveResult resolveResult) {
List<PsiClassType> result = ContainerUtil.newArrayList();
PsiMethod method = (PsiMethod)resolveResult.getElement();
List<PsiClassType> result = new ArrayList<PsiClassType>();
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<PsiClassType> getExceptionsByMethod(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) {
List<PsiClassType> 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<PsiClassType> collectUnhandledExceptions(@NotNull PsiElement element,
@Nullable PsiElement topElement) {
public static Collection<PsiClassType> collectUnhandledExceptions(@NotNull PsiElement element, @Nullable PsiElement topElement) {
return collectUnhandledExceptions(element, topElement, true);
}
@NotNull
public static Collection<PsiClassType> collectUnhandledExceptions(@NotNull PsiElement element,
@Nullable PsiElement topElement, boolean includeSelfCalls) {
@Nullable PsiElement topElement,
boolean includeSelfCalls) {
final Set<PsiClassType> set = collectUnhandledExceptions(element, topElement, null, includeSelfCalls);
return set == null ? Collections.<PsiClassType>emptyList() : set;
}
@Nullable
private static Set<PsiClassType> collectUnhandledExceptions(@NotNull PsiElement element,
PsiElement topElement,
@Nullable Set<PsiClassType> foundExceptions, boolean includeSelfCalls) {
@Nullable PsiElement topElement,
@Nullable Set<PsiClassType> foundExceptions,
boolean includeSelfCalls) {
Collection<PsiClassType> unhandledExceptions = null;
if (element instanceof PsiCallExpression) {
PsiCallExpression expression = (PsiCallExpression)element;
@@ -303,7 +320,7 @@ public class ExceptionUtil {
@NotNull
public static List<PsiClassType> getUnhandledExceptions(PsiElement[] elements) {
final List<PsiClassType> array = new ArrayList<PsiClassType>();
final List<PsiClassType> 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<PsiClassType> 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<PsiClassType> getCloserExceptions(final PsiResourceVariable resource) {
final PsiMethod method = PsiUtil.getResourceCloserMethod(resource);
return method != null ? getExceptionsByMethod(method, PsiSubstitutor.EMPTY) : Collections.<PsiClassType>emptyList();
}
return Collections.emptyList();
@NotNull
public static List<PsiClassType> getUnhandledCloserExceptions(final PsiResourceVariable resource, @Nullable final PsiElement topElement) {
final PsiMethod method = PsiUtil.getResourceCloserMethod(resource);
return method != null ? getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY) : Collections.<PsiClassType>emptyList();
}
@NotNull
@@ -444,7 +448,7 @@ public class ExceptionUtil {
}
final PsiClassType[] referencedTypes = method.getThrowsList().getReferencedTypes();
if (referencedTypes.length > 0) {
List<PsiClassType> result = new ArrayList<PsiClassType>();
List<PsiClassType> 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<PsiClass>() {
@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<PsiClass>() {
@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) {
@@ -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<PsiClassType> 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<PsiClassType> 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<PsiType> uncaughtTypes = ContainerUtil.mapNotNull(thrownTypes, new NullableFunction<PsiClassType, PsiType>() {
@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<PsiType> uncaughtTypes = ContainerUtil.mapNotNull(thrownTypes, new NullableFunction<PsiClassType, PsiType>() {
@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
@@ -156,7 +156,7 @@ class C {
}
}
void m11() throws E1 {
void m11() {
try {
System.out.println();
}
@@ -165,4 +165,22 @@ class C {
<error descr="Unhandled exception: java.lang.Exception">throw e;</error>
}
}
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
<error descr="Unhandled exceptions: C.E1, C.E2">throw e;</error>
}
}
}
@@ -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<PsiResourceVariable> resourceVariables = resourceList.getResourceVariables();
for (PsiResourceVariable resourceVariable : resourceVariables) {
final Set<PsiClassType> 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<PsiType> exceptionsHandled, PsiType thrownType) {
for (PsiType exceptionHandled : exceptionsHandled) {
if (exceptionHandled.isAssignableFrom(thrownType)) {