Try-with-resource support: highlight unhandled exceptions from close()

This commit is contained in:
Roman Shevchenko
2011-02-22 20:33:24 +01:00
parent 07bc7abdce
commit 5fa72a60da
5 changed files with 86 additions and 52 deletions
@@ -247,18 +247,12 @@ public class ExceptionUtil {
}
if (element instanceof PsiResource) {
final PsiType resourceType = ((PsiResource)element).getType();
if (resourceType instanceof PsiClassType) {
final PsiClass resourceClass = ((PsiClassType)resourceType).resolve();
if (resourceClass != null) {
final List<PsiClassType> unhandled = getUnhandledCloserExceptions(element, resourceClass, topElement);
if (unhandledExceptions == null) {
unhandledExceptions = unhandled;
}
else {
unhandledExceptions.addAll(unhandled);
}
}
final List<PsiClassType> unhandled = getUnhandledCloserExceptions((PsiResource)element, topElement);
if (unhandledExceptions == null) {
unhandledExceptions = unhandled;
}
else {
unhandledExceptions.addAll(unhandled);
}
}
@@ -337,20 +331,24 @@ public class ExceptionUtil {
}
@NotNull
public static List<PsiClassType> getUnhandledCloserExceptions(final PsiElement resource,
final PsiClass resourceClass,
final PsiElement topElement) {
final PsiMethod[] closers = resourceClass.findMethodsByName("close", false);
for (final PsiMethod method : closers) {
if (method.getParameterList().getParametersCount() == 0) {
return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY);
public static List<PsiClassType> getUnhandledCloserExceptions(final PsiResource resource, final PsiElement topElement) {
final PsiType resourceType = resource.getType();
if (resourceType instanceof PsiClassType) {
final PsiClass resourceClass = ((PsiClassType)resourceType).resolve();
if (resourceClass != null) {
final PsiMethod[] closers = resourceClass.findMethodsByName("close", false);
for (final PsiMethod method : closers) {
if (method.getParameterList().getParametersCount() == 0) {
return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY);
}
}
}
}
return Collections.emptyList();
}
@Nullable
public static PsiClassType getUnhandledException(PsiThrowStatement throwStatement, PsiElement topElement){
public static PsiClassType getUnhandledException(PsiThrowStatement throwStatement, PsiElement topElement) {
final PsiExpression exception = throwStatement.getException();
if (exception != null) {
final PsiType type = exception.getType();
@@ -36,6 +36,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
import com.intellij.psi.javadoc.PsiDocComment;
@@ -45,6 +46,7 @@ import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.xml.util.XmlStringUtil;
import gnu.trove.THashMap;
@@ -543,14 +545,16 @@ public class HighlightUtil {
return errorResult;
}
public static String getUnhandledExceptionsDescriptor(Collection<PsiClassType> unhandledExceptions) {
StringBuilder exceptionsText = new StringBuilder();
for (PsiClassType unhandledException : unhandledExceptions) {
if (exceptionsText.length() != 0) exceptionsText.append(", ");
exceptionsText.append(formatType(unhandledException));
}
public static String getUnhandledExceptionsDescriptor(final Collection<PsiClassType> unhandled) {
return getUnhandledExceptionsDescriptor(unhandled, null);
}
return JavaErrorMessages.message("unhandled.exceptions", exceptionsText.toString(), unhandledExceptions.size());
private static String getUnhandledExceptionsDescriptor(final Collection<PsiClassType> unhandled, final String source) {
final String exceptions = StringUtil.join(unhandled, new Function<PsiClassType, String>() {
@Override public String fun(PsiClassType type) { return formatType(type); }
}, ", ");
return source != null ? JavaErrorMessages.message("unhandled.close.exceptions", exceptions, unhandled.size(), source)
: JavaErrorMessages.message("unhandled.exceptions", exceptions, unhandled.size());
}
@Nullable
@@ -633,26 +637,45 @@ public class HighlightUtil {
@Nullable
public static HighlightInfo checkUnhandledExceptions(PsiElement element, TextRange fixRange) {
List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element);
HighlightInfo errorResult = null;
if (!unhandledExceptions.isEmpty()) {
if (fixRange == null) {
fixRange = element.getTextRange();
}
HighlightInfoType highlightType = getUnhandledExceptionHighlightType(element);
if (highlightType == null) return null;
errorResult = HighlightInfo.createHighlightInfo(highlightType, fixRange, getUnhandledExceptionsDescriptor(unhandledExceptions));
QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToCatchFix());
QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToThrowsFix(element));
QuickFixAction.registerQuickFixAction(errorResult, new SurroundWithTryCatchFix(element));
if (unhandledExceptions.size() == 1) {
QuickFixAction.registerQuickFixAction(errorResult, new GeneralizeCatchFix(element, unhandledExceptions.get(0)));
}
}
public static HighlightInfo checkUnhandledExceptions(final PsiElement element, TextRange fixRange) {
final List<PsiClassType> unhandledExceptions = ExceptionUtil.getUnhandledExceptions(element);
if (unhandledExceptions.isEmpty()) return null;
final HighlightInfoType highlightType = getUnhandledExceptionHighlightType(element);
if (highlightType == null) return null;
if (fixRange == null) fixRange = element.getTextRange();
final String description = getUnhandledExceptionsDescriptor(unhandledExceptions);
final HighlightInfo errorResult = HighlightInfo.createHighlightInfo(highlightType, fixRange, description);
registerUnhandledExceptionFixes(element, errorResult, unhandledExceptions);
return errorResult;
}
@Nullable
public static HighlightInfo checkUnhandledCloserExceptions(final PsiResource resource) {
final List<PsiClassType> unhandled = ExceptionUtil.getUnhandledCloserExceptions(resource, null);
if (unhandled.isEmpty()) return null;
final HighlightInfoType highlightType = getUnhandledExceptionHighlightType(resource);
if (highlightType == null) return null;
final String description = getUnhandledExceptionsDescriptor(unhandled, "auto-closeable resource");
final HighlightInfo highlight = HighlightInfo.createHighlightInfo(highlightType, resource, description);
registerUnhandledExceptionFixes(resource, highlight, unhandled);
return highlight;
}
private static void registerUnhandledExceptionFixes(final PsiElement element,
final HighlightInfo errorResult,
final List<PsiClassType> unhandled) {
QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToCatchFix());
QuickFixAction.registerQuickFixAction(errorResult, new AddExceptionToThrowsFix(element));
QuickFixAction.registerQuickFixAction(errorResult, new SurroundWithTryCatchFix(element));
if (unhandled.size() == 1) {
QuickFixAction.registerQuickFixAction(errorResult, new GeneralizeCatchFix(element, unhandled.get(0)));
}
}
@Nullable
private static HighlightInfoType getUnhandledExceptionHighlightType(final PsiElement element) {
if (!JspPsiUtil.isInJspFile(element)) {
@@ -902,13 +902,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightUtil.checkCatchParameterIsThrowable(parameter));
myHolder.add(GenericsHighlightUtil.checkCatchParameterIsClass(parameter));
}
}
}
PsiResourceList resources = statement.getResourceList();
if (resources != null) {
for (PsiResource resource : resources.getResources()) {
myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(resource));
}
}
@Override
public void visitResource(final PsiResource resource) {
myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(resource));
if (!myHolder.hasErrorResults()) {
myHolder.add(HighlightUtil.checkUnhandledCloserExceptions(resource));
}
}
@@ -16,6 +16,16 @@ class C {
try (new MyResource()) { }
catch (E1 | E3 ignore) { }
MyResource r;
try (<error descr="Unhandled exception from auto-closeable resource: C.E3">r = new MyResource()</error>) { }
catch (E1 e) { }
try (r = <error descr="Unhandled exception: C.E1">new MyResource()</error>) { }
catch (E3 e) { }
try (r = <error descr="Unhandled exception: C.E1">new MyResource()</error>) { }
}
void m2() throws Exception {
@@ -27,6 +37,7 @@ class C {
void m3() throws Exception {
try (MyResource r = new MyResource()) {
r.doSomething();
/* todo: < error descr="Cannot assign a value to final variable 'r'">r = null</error >;*/
}
catch (E e) {
<error descr="Cannot resolve symbol 'r'">r</error> = null;
@@ -184,8 +184,10 @@ return.outside.method=Return outside method
return.from.void.method=Cannot return a value from a method with void result type
missing.return.value=Missing return value
#{0] - exceptions list (comma separated). {1} - exceptions count in the list
#{0} - exceptions list (comma separated), {1} - exceptions count in the list, {2} - exception source
unhandled.exceptions=Unhandled {1, choice, 0#exception|2#exceptions}: {0}
unhandled.close.exceptions=Unhandled {1, choice, 0#exception|2#exceptions} from {2}: {0}
variable.already.defined=Variable ''{0}'' is already defined in the scope
break.outside.switch.or.loop=Break outside switch or loop
continue.outside.loop=Continue outside of loop
@@ -212,7 +214,6 @@ duplicate.default.switch.label=Duplicate default label
duplicate.switch.label=Duplicate label ''{0}''
switch.colon.expected.after.case.label=':' expected
#See JLS 8.3.2.3
illegal.forward.reference=Illegal forward reference
unknown.class=Unknown class: ''{0}''