Project Coin precise rethrow support

This commit is contained in:
Roman Shevchenko
2011-03-17 17:47:38 +01:00
parent 084c70b827
commit 91c7d40078
10 changed files with 469 additions and 58 deletions
@@ -22,7 +22,10 @@ import com.intellij.psi.*;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
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;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -67,7 +70,7 @@ public class ExceptionUtil {
}
@NotNull
private static List<PsiClassType> getThrownExceptions(@NotNull PsiElement element) {
public static List<PsiClassType> getThrownExceptions(@NotNull PsiElement element) {
if (element instanceof PsiClass) {
if (element instanceof PsiAnonymousClass) {
final PsiExpressionList argumentList = ((PsiAnonymousClass)element).getArgumentList();
@@ -88,15 +91,14 @@ public class ExceptionUtil {
return getExceptionsByMethodAndChildren(element, result);
}
else if (element instanceof PsiThrowStatement) {
PsiExpression expr = ((PsiThrowStatement)element).getException();
final PsiExpression expr = ((PsiThrowStatement)element).getException();
if (expr == null) return Collections.emptyList();
PsiType exception = expr.getType();
List<PsiClassType> array = new ArrayList<PsiClassType>();
if (exception instanceof PsiClassType) {
array.add((PsiClassType)exception);
}
addExceptions(array, getThrownExceptions(expr));
return array;
final List<PsiType> types = getPreciseThrowTypes(expr);
final List<PsiClassType> classTypes = ContainerUtil.mapNotNull(types, new NullableFunction<PsiType, PsiClassType>() {
@Override public PsiClassType fun(PsiType type) { return type instanceof PsiClassType ? (PsiClassType)type : null; }
});
addExceptions(classTypes, getThrownExceptions(expr));
return classTypes;
}
else if (element instanceof PsiTryStatement) {
PsiTryStatement tryStatement = (PsiTryStatement)element;
@@ -203,8 +205,7 @@ public class ExceptionUtil {
}
else if (element instanceof PsiThrowStatement) {
PsiThrowStatement statement = (PsiThrowStatement)element;
PsiClassType exception = getUnhandledException(statement, topElement);
unhandledExceptions = exception == null ? Collections.<PsiClassType>emptyList() : Collections.singletonList(exception);
unhandledExceptions = getUnhandledExceptions(statement, topElement);
}
else if (element instanceof PsiCodeBlock &&
element.getParent() instanceof PsiMethod &&
@@ -293,7 +294,7 @@ public class ExceptionUtil {
@Override
public void visitThrowStatement(PsiThrowStatement statement) {
addException(array, getUnhandledException(statement, null));
addExceptions(array, getUnhandledExceptions(statement, null));
visitElement(statement);
}
@@ -319,14 +320,13 @@ public class ExceptionUtil {
}
else if (element instanceof PsiThrowStatement) {
PsiThrowStatement throwStatement = (PsiThrowStatement)element;
PsiClassType exception = getUnhandledException(throwStatement, null);
if (exception != null) return Collections.singletonList(exception);
return getUnhandledExceptions(throwStatement, null);
}
else if (element instanceof PsiResourceVariable) {
return getUnhandledCloserExceptions((PsiResourceVariable)element, null);
}
return Collections.emptyList();
return getUnhandledExceptions(new PsiElement[]{element});
}
@NotNull
@@ -358,19 +358,41 @@ public class ExceptionUtil {
return Collections.emptyList();
}
@Nullable
public static PsiClassType getUnhandledException(PsiThrowStatement throwStatement, PsiElement topElement) {
@NotNull
public static List<PsiClassType> getUnhandledExceptions(final PsiThrowStatement throwStatement, final PsiElement topElement) {
final PsiExpression exception = throwStatement.getException();
if (exception != null) {
final PsiType type = exception.getType();
if (type instanceof PsiClassType) {
PsiClassType classType = (PsiClassType)type;
if (!isUncheckedException(classType) && !isHandled(throwStatement, classType, topElement)) {
return classType;
final List<PsiType> types = getPreciseThrowTypes(exception);
return ContainerUtil.mapNotNull(types, new NullableFunction<PsiType, PsiClassType>() {
@Override
public PsiClassType fun(PsiType type) {
if (type instanceof PsiClassType) {
final PsiClassType classType = (PsiClassType)type;
if (!isUncheckedException(classType) && !isHandled(throwStatement, classType, topElement)) {
return classType;
}
}
return null;
}
});
}
@NotNull
private static List<PsiType> getPreciseThrowTypes(@Nullable final PsiExpression expression) {
if (expression instanceof PsiReferenceExpression) {
final PsiElement target = ((PsiReferenceExpression)expression).resolve();
if (target != null && PsiUtil.isCatchParameter(target)) {
return ((PsiCatchSection)target.getParent()).getPreciseCatchTypes();
}
}
return null;
if (expression != null) {
final PsiType type = expression.getType();
if (type != null) {
return Arrays.asList(type);
}
}
return Collections.emptyList();
}
@NotNull
@@ -433,11 +455,11 @@ public class ExceptionUtil {
}
final PsiClass errorClass = ApplicationManager.getApplication().runReadAction(
new NullableComputable<PsiClass>() {
public PsiClass compute() {
return JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Error", searchScope);
}
new NullableComputable<PsiClass>() {
public PsiClass compute() {
return JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Error", searchScope);
}
}
);
return errorClass != null && InheritanceUtil.isInheritorOrSelf(aClass, errorClass, true);
}
@@ -526,14 +548,9 @@ public class ExceptionUtil {
}
private static boolean isCaught(PsiTryStatement tryStatement, PsiClassType exceptionType) {
// if finally block completes abruptly, exception gets lost
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
if (finallyBlock != null) {
List<PsiClassType> exceptions = getUnhandledExceptions(finallyBlock);
if (exceptions.contains(exceptionType)) return false;
// if finally block completes normally, exception not caught
// if finally block completes abruptly, exception gets lost
if (blockCompletesAbruptly(finallyBlock)) return true;
}
if (finallyBlock != null && blockCompletesAbruptly(finallyBlock)) return true;
final PsiParameter[] catchBlockParameters = tryStatement.getCatchBlockParameters();
for (PsiParameter parameter : catchBlockParameters) {
@@ -178,7 +178,7 @@ public class AddExceptionToThrowsFix extends BaseIntentionAction {
@Nullable
private static PsiElement findElement(PsiElement element, PsiMethod topElement) {
if (element == null) return null;
if (element == null || element == topElement) return null;
List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element);
if (!filterInProjectExceptions(topElement, unhandledExceptions).isEmpty()) {
return element;
@@ -68,29 +68,34 @@ public class HighlightExceptionsHandler extends HighlightUsagesHandlerBase<PsiCl
private void addExceptionThrownPlaces(final PsiType type) {
if (type instanceof PsiClassType) {
myPlace.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
}
@Override public void visitThrowStatement(PsiThrowStatement statement) {
@Override
public void visitThrowStatement(PsiThrowStatement statement) {
super.visitThrowStatement(statement);
PsiClassType actualType = ExceptionUtil.getUnhandledException(statement, myPlace);
if (actualType != null && type.isAssignableFrom(actualType) && myTypeFilter.value(actualType)) {
PsiExpression psiExpression = statement.getException();
if (psiExpression instanceof PsiReferenceExpression) {
addOccurrence(psiExpression);
}
else if (psiExpression instanceof PsiNewExpression) {
PsiJavaCodeReferenceElement ref = ((PsiNewExpression)psiExpression).getClassReference();
addOccurrence(ref);
}
else {
addOccurrence(statement.getException());
final List<PsiClassType> actualTypes = ExceptionUtil.getUnhandledExceptions(statement, myPlace);
for (PsiClassType actualType : actualTypes) {
if (actualType != null && type.isAssignableFrom(actualType) && myTypeFilter.value(actualType)) {
PsiExpression psiExpression = statement.getException();
if (psiExpression instanceof PsiReferenceExpression) {
addOccurrence(psiExpression);
}
else if (psiExpression instanceof PsiNewExpression) {
PsiJavaCodeReferenceElement ref = ((PsiNewExpression)psiExpression).getClassReference();
addOccurrence(ref);
}
else {
addOccurrence(statement.getException());
}
}
}
}
@Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
PsiReference reference = expression.getMethodExpression().getReference();
if (reference == null) return;
@@ -103,7 +108,8 @@ public class HighlightExceptionsHandler extends HighlightUsagesHandlerBase<PsiCl
}
}
@Override public void visitNewExpression(PsiNewExpression expression) {
@Override
public void visitNewExpression(PsiNewExpression expression) {
super.visitNewExpression(expression);
PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
if (classReference == null) return;
@@ -15,25 +15,36 @@
*/
package com.intellij.psi.impl.source.tree.java;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ChildRoleBase;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.NullableFunction;
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;
/**
* @author ven
*/
public class PsiCatchSectionImpl extends CompositePsiElement implements PsiCatchSection, Constants {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiCatchSectionImpl");
private CachedValue<List<PsiType>> myTypesCache = null;
public PsiCatchSectionImpl() {
super(CATCH_SECTION);
}
@@ -52,6 +63,85 @@ public class PsiCatchSectionImpl extends CompositePsiElement implements PsiCatch
return parameter.getType();
}
@NotNull
public List<PsiType> getPreciseCatchTypes() {
final PsiParameter parameter = getParameter();
if (parameter == null) return Collections.emptyList();
return getTypesCache().getValue();
}
private synchronized CachedValue<List<PsiType>> getTypesCache() {
if (myTypesCache == null) {
final CachedValuesManager cacheManager = CachedValuesManager.getManager(getProject());
myTypesCache = cacheManager.createCachedValue(new CachedValueProvider<List<PsiType>>() {
@Override public Result<List<PsiType>> compute() {
final List<PsiType> types = computePreciseCatchTypes(getParameter());
return Result.create(types, PsiModificationTracker.MODIFICATION_COUNT);
}
}, false);
}
return myTypesCache;
}
private List<PsiType> computePreciseCatchTypes(final PsiParameter parameter) {
final PsiType declaredType = parameter.getType();
// When the thrown expression is a ... exception parameter Ej (parameter) of a catch clause Cj (this) ...
final LanguageLevel level = PsiUtil.getLanguageLevel(parameter);
if (level.isAtLeast(LanguageLevel.JDK_1_7)) {
if (isCatchParameterEffectivelyFinal(parameter, getCatchBlock())) {
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);
// ... 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 && thrownTypes.size() > 0; i++) {
final PsiType catchType = parameters[i].getType();
if (catchType.isAssignableFrom(thrownType)) return null;
}
return thrownType;
}
});
// ... and T is assignable to Ej ...
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);
}
// do not use control flow here to avoid dead loop
private static boolean isCatchParameterEffectivelyFinal(final PsiParameter parameter, final PsiCodeBlock catchBlock) {
final boolean[] result = {true};
if (catchBlock != null) {
catchBlock.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitAssignmentExpression(final PsiAssignmentExpression expression) {
final PsiExpression left = expression.getLExpression();
if (left instanceof PsiReferenceExpression && parameter.equals(((PsiReferenceExpression)left).resolve())) {
result[0] = false;
stopWalking();
}
}
});
}
return result[0];
}
@NotNull
public PsiTryStatement getTryStatement() {
return (PsiTryStatement)getParent();