mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Fix precise type calculation for try-with-resources
This commit is contained in:
@@ -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) {
|
||||
|
||||
+32
-28
@@ -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
|
||||
|
||||
+19
-1
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user