exceptions declared by try with resources should collect all close methods from the resource class and choose exceptions in the intersection (IDEA-158762)

This commit is contained in:
Anna.Kozlova
2016-07-21 13:47:22 +02:00
parent 10c0da8451
commit d8def3334c
7 changed files with 120 additions and 73 deletions
@@ -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<PsiClassType> 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());
}
}
@@ -100,11 +100,6 @@ public class TryWithResourcesPostfixTemplate extends PostfixTemplate {
@NotNull
private static Collection<PsiClassType> 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.<PsiClassType>emptyList();
return ExceptionUtil.getUnhandledCloserExceptions(expression, null, expression.getType());
}
}
@@ -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
@@ -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<PsiClassType> getCloserExceptions(@NotNull PsiResourceListElement resource) {
Pair<PsiMethod, PsiSubstitutor> closer = resolveCloser(resource);
return closer != null ? getExceptionsByMethod(closer.first, closer.second, resource) : Collections.<PsiClassType>emptyList();
List<PsiClassType> ex = getExceptionsFromClose(resource);
return ex != null ? ex : Collections.<PsiClassType>emptyList();
}
@NotNull
public static List<PsiClassType> getUnhandledCloserExceptions(@NotNull PsiResourceListElement resource, @Nullable PsiElement topElement) {
Pair<PsiMethod, PsiSubstitutor> closer = resolveCloser(resource);
return closer != null ? getUnhandledExceptions(closer.first, resource, topElement, closer.second) : Collections.<PsiClassType>emptyList();
final PsiType type = resource.getType();
return getUnhandledCloserExceptions(resource, topElement, type);
}
private static Pair<PsiMethod, PsiSubstitutor> 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<PsiClassType> getUnhandledCloserExceptions(PsiElement place, @Nullable PsiElement topElement, PsiType type) {
List<PsiClassType> ex = type instanceof PsiClassType ? getExceptionsFromClose(type, place.getResolveScope()) : null;
return ex != null ? getUnhandledExceptions(place, topElement, PsiSubstitutor.EMPTY, ex.toArray(new PsiClassType[ex.size()])) : Collections.<PsiClassType>emptyList();
}
private static List<PsiClassType> getExceptionsFromClose(PsiResourceListElement resource) {
final PsiType type = resource.getType();
return type instanceof PsiClassType ? getExceptionsFromClose(type, resource.getResolveScope()) : null;
}
private static List<PsiClassType> 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<PsiClassType> 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;
@@ -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<T extends Throwable> {
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<IOException> {}
class Main {
{
try (<error descr="Unhandled exception from auto-closeable resource: java.io.IOException">I1 i1 = null</error>) {}
try (I1 i11 = null) {
} catch (IOException e){}
try (I1R i11r = null) {
} catch (IOException e){}
try (I2 i2 = null) {}
try (I2 i21 = null) {
} catch (<error descr="Exception 'java.io.IOException' is never thrown in the corresponding try block">IOException e</error>) {}
try (I2 i22 = null) {
} catch (<error descr="Exception 'Ex' is never thrown in the corresponding try block">Ex e</error>) {}
try (I3 i3 = null) {}
try (I3R i3r = null) {}
try (<error descr="Unhandled exception from auto-closeable resource: java.io.IOException">IT it = null</error>) {}
}
}
@@ -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()));
@@ -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<PsiClassType> 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<PsiType> getExceptionTypesHandled(PsiTryStatement statement) {
final Set<PsiType> 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