/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.codeInsight; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.controlFlow.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.infos.CandidateInfo; import com.intellij.psi.scope.MethodProcessorSetupFailedException; import com.intellij.psi.scope.processor.MethodResolverProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.util.*; import com.intellij.util.Function; import com.intellij.util.NullableFunction; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; /** * @author mike */ public class ExceptionUtil { @NonNls private static final String CLONE_METHOD_NAME = "clone"; private ExceptionUtil() {} @NotNull public static List<PsiClassType> getThrownExceptions(@NotNull PsiElement[] elements) { List<PsiClassType> array = ContainerUtil.newArrayList(); for (PsiElement element : elements) { List<PsiClassType> exceptions = getThrownExceptions(element); addExceptions(array, exceptions); } return array; } @NotNull public static List<PsiClassType> getThrownCheckedExceptions(@NotNull PsiElement[] elements) { List<PsiClassType> exceptions = getThrownExceptions(elements); if (exceptions.isEmpty()) return exceptions; exceptions = filterOutUncheckedExceptions(exceptions); return exceptions; } @NotNull private static List<PsiClassType> filterOutUncheckedExceptions(@NotNull List<PsiClassType> exceptions) { List<PsiClassType> array = ContainerUtil.newArrayList(); for (PsiClassType exception : exceptions) { if (!isUncheckedException(exception)) array.add(exception); } return array; } @NotNull public static List<PsiClassType> getThrownExceptions(@NotNull PsiElement element) { if (element instanceof PsiClass) { if (element instanceof PsiAnonymousClass) { final PsiExpressionList argumentList = ((PsiAnonymousClass)element).getArgumentList(); if (argumentList != null){ return getThrownExceptions(argumentList); } } // filter class declaration in code return Collections.emptyList(); } else if (element instanceof PsiMethodCallExpression) { PsiReferenceExpression methodRef = ((PsiMethodCallExpression)element).getMethodExpression(); JavaResolveResult result = methodRef.advancedResolve(false); return getExceptionsByMethodAndChildren(element, result); } else if (element instanceof PsiNewExpression) { JavaResolveResult result = ((PsiNewExpression)element).resolveMethodGenerics(); return getExceptionsByMethodAndChildren(element, result); } else if (element instanceof PsiThrowStatement) { final PsiExpression expr = ((PsiThrowStatement)element).getException(); if (expr == null) return Collections.emptyList(); final List<PsiType> types = getPreciseThrowTypes(expr); List<PsiClassType> classTypes = new ArrayList<PsiClassType>(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) { return getTryExceptions((PsiTryStatement)element); } else if (element instanceof PsiResourceVariable) { final PsiResourceVariable variable = (PsiResourceVariable)element; final List<PsiClassType> types = ContainerUtil.newArrayList(); addExceptions(types, getCloserExceptions(variable)); final PsiExpression initializer = variable.getInitializer(); if (initializer != null) addExceptions(types, getThrownExceptions(initializer)); return types; } return getThrownExceptions(element.getChildren()); } @NotNull private static List<PsiClassType> getTryExceptions(@NotNull PsiTryStatement tryStatement) { List<PsiClassType> array = ContainerUtil.newArrayList(); PsiResourceList resourceList = tryStatement.getResourceList(); if (resourceList != null) { for (PsiResourceVariable variable : resourceList.getResourceVariables()) { addExceptions(array, getUnhandledCloserExceptions(variable, resourceList)); } } PsiCodeBlock tryBlock = tryStatement.getTryBlock(); if (tryBlock != null) { addExceptions(array, getThrownExceptions(tryBlock)); } for (PsiParameter parameter : tryStatement.getCatchBlockParameters()) { PsiType exception = parameter.getType(); for (int j = array.size() - 1; j >= 0; j--) { PsiClassType exception1 = array.get(j); if (exception.isAssignableFrom(exception1)) { array.remove(exception1); } } } for (PsiCodeBlock catchBlock : tryStatement.getCatchBlocks()) { addExceptions(array, getThrownExceptions(catchBlock)); } PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (finallyBlock != null) { // if finally block completes normally, exception not caught // if finally block completes abruptly, exception gets lost try { ControlFlow flow = ControlFlowFactory .getInstance(finallyBlock.getProject()).getControlFlow(finallyBlock, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); int completionReasons = ControlFlowUtil.getCompletionReasons(flow, 0, flow.getSize()); List<PsiClassType> thrownExceptions = getThrownExceptions(finallyBlock); if ((completionReasons & ControlFlowUtil.NORMAL_COMPLETION_REASON) == 0) { array = ContainerUtil.newArrayList(thrownExceptions); } else { addExceptions(array, thrownExceptions); } } catch (AnalysisCanceledException e) { // incomplete code } } return array; } @NotNull private static List<PsiClassType> getExceptionsByMethodAndChildren(@NotNull PsiElement element, @NotNull JavaResolveResult resolveResult) { List<PsiClassType> result = ContainerUtil.newArrayList(); PsiMethod method = (PsiMethod)resolveResult.getElement(); if (method != null) { 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); } } return result; } private static void addExceptions(@NotNull List<PsiClassType> array, @NotNull Collection<PsiClassType> exceptions) { for (PsiClassType exception : exceptions) { addException(array, exception); } } private static void addException(@NotNull List<PsiClassType> array, @Nullable PsiClassType exception) { if (exception == null) return ; for (int i = array.size()-1; i>=0; i--) { PsiClassType exception1 = array.get(i); if (exception1.isAssignableFrom(exception)) return; if (exception.isAssignableFrom(exception1)) { array.remove(i); } } array.add(exception); } @NotNull 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) { 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, @Nullable PsiElement topElement, @Nullable Set<PsiClassType> foundExceptions, boolean includeSelfCalls) { Collection<PsiClassType> unhandledExceptions = null; if (element instanceof PsiCallExpression) { PsiCallExpression expression = (PsiCallExpression)element; unhandledExceptions = getUnhandledExceptions(expression, topElement, includeSelfCalls); } else if (element instanceof PsiMethodReferenceExpression) { unhandledExceptions = getUnhandledExceptions((PsiMethodReferenceExpression)element, topElement); } else if (element instanceof PsiThrowStatement) { PsiThrowStatement statement = (PsiThrowStatement)element; unhandledExceptions = getUnhandledExceptions(statement, topElement); } else if (element instanceof PsiCodeBlock && element.getParent() instanceof PsiMethod && ((PsiMethod)element.getParent()).isConstructor() && !firstStatementIsConstructorCall((PsiCodeBlock)element)) { // there is implicit parent constructor call final PsiMethod constructor = (PsiMethod)element.getParent(); final PsiClass aClass = constructor.getContainingClass(); final PsiClass superClass = aClass == null ? null : aClass.getSuperClass(); final PsiMethod[] superConstructors = superClass == null ? PsiMethod.EMPTY_ARRAY : superClass.getConstructors(); Set<PsiClassType> unhandled = new HashSet<PsiClassType>(); for (PsiMethod superConstructor : superConstructors) { if (!superConstructor.hasModifierProperty(PsiModifier.PRIVATE) && superConstructor.getParameterList().getParametersCount() == 0) { final PsiClassType[] exceptionTypes = superConstructor.getThrowsList().getReferencedTypes(); for (PsiClassType exceptionType : exceptionTypes) { if (!isUncheckedException(exceptionType) && !isHandled(element, exceptionType, topElement)) { unhandled.add(exceptionType); } } break; } } // plus all exceptions thrown in instance class initializers if (aClass != null) { final PsiClassInitializer[] initializers = aClass.getInitializers(); final Set<PsiClassType> thrownByInitializer = new THashSet<PsiClassType>(); for (PsiClassInitializer initializer : initializers) { if (initializer.hasModifierProperty(PsiModifier.STATIC)) continue; thrownByInitializer.clear(); collectUnhandledExceptions(initializer.getBody(), initializer, thrownByInitializer, includeSelfCalls); for (PsiClassType thrown : thrownByInitializer) { if (!isHandled(constructor.getBody(), thrown, topElement)) { unhandled.add(thrown); } } } } unhandledExceptions = unhandled; } if (element instanceof PsiResourceVariable) { final List<PsiClassType> unhandled = getUnhandledCloserExceptions((PsiResourceVariable)element, topElement); if (!unhandled.isEmpty()) { if (unhandledExceptions == null) { unhandledExceptions = ContainerUtil.newArrayList(unhandled); } else { unhandledExceptions.addAll(unhandled); } } } if (unhandledExceptions != null) { if (foundExceptions == null) { foundExceptions = new THashSet<PsiClassType>(); } foundExceptions.addAll(unhandledExceptions); } for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { foundExceptions = collectUnhandledExceptions(child, topElement, foundExceptions, includeSelfCalls); } return foundExceptions; } @NotNull private static Collection<PsiClassType> getUnhandledExceptions(@NotNull PsiMethodReferenceExpression methodReferenceExpression, PsiElement topElement) { final JavaResolveResult resolveResult = methodReferenceExpression.advancedResolve(false); final PsiElement resolve = resolveResult.getElement(); if (resolve instanceof PsiMethod) { return getUnhandledExceptions((PsiMethod)resolve, methodReferenceExpression, topElement, resolveResult.getSubstitutor()); } return Collections.emptyList(); } private static boolean firstStatementIsConstructorCall(@NotNull PsiCodeBlock constructorBody) { final PsiStatement[] statements = constructorBody.getStatements(); if (statements.length == 0) return false; if (!(statements[0] instanceof PsiExpressionStatement)) return false; final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression(); if (!(expression instanceof PsiMethodCallExpression)) return false; final PsiMethod method = (PsiMethod)((PsiMethodCallExpression)expression).getMethodExpression().resolve(); return method != null && method.isConstructor(); } @NotNull public static List<PsiClassType> getUnhandledExceptions(@NotNull PsiElement[] elements) { final List<PsiClassType> array = ContainerUtil.newArrayList(); final PsiElementVisitor visitor = new JavaRecursiveElementWalkingVisitor() { @Override public void visitCallExpression(@NotNull PsiCallExpression expression) { addExceptions(array, getUnhandledExceptions(expression, null)); visitElement(expression); } @Override public void visitThrowStatement(@NotNull PsiThrowStatement statement) { addExceptions(array, getUnhandledExceptions(statement, null)); visitElement(statement); } @Override public void visitMethodReferenceExpression(@NotNull PsiMethodReferenceExpression expression) { addExceptions(array, getUnhandledExceptions(expression, null)); visitElement(expression); } @Override public void visitResourceVariable(@NotNull PsiResourceVariable resourceVariable) { addExceptions(array, getUnhandledCloserExceptions(resourceVariable, null)); visitElement(resourceVariable); } }; for (PsiElement element : elements) { element.accept(visitor); } return array; } @NotNull public static List<PsiClassType> getUnhandledExceptions(PsiElement element) { if (element instanceof PsiCallExpression) { PsiCallExpression expression = (PsiCallExpression)element; return getUnhandledExceptions(expression, null); } else if (element instanceof PsiThrowStatement) { PsiThrowStatement throwStatement = (PsiThrowStatement)element; return getUnhandledExceptions(throwStatement, null); } else if (element instanceof PsiResourceVariable) { return getUnhandledCloserExceptions((PsiResourceVariable)element, null); } return getUnhandledExceptions(new PsiElement[]{element}); } @NotNull public static List<PsiClassType> getUnhandledExceptions(@NotNull final PsiCallExpression methodCall, @Nullable final PsiElement topElement) { return getUnhandledExceptions(methodCall, topElement, true); } @NotNull public static List<PsiClassType> getUnhandledExceptions(@NotNull final PsiCallExpression methodCall, @Nullable final PsiElement topElement, final boolean includeSelfCalls) { final JavaResolveResult result = methodCall.resolveMethodGenerics(); final PsiMethod method = (PsiMethod)result.getElement(); final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class); if (!includeSelfCalls && method == containingMethod) { return Collections.emptyList(); } final PsiSubstitutor substitutor = result.getSubstitutor(); if (method != null && !isArrayClone(method, methodCall) && methodCall instanceof PsiMethodCallExpression) { final PsiClassType[] thrownExceptions = method.getThrowsList().getReferencedTypes(); if (thrownExceptions.length > 0) { final PsiFile containingFile = (containingMethod == null ? methodCall : containingMethod).getContainingFile(); final MethodResolverProcessor processor = new MethodResolverProcessor((PsiMethodCallExpression)methodCall, containingFile); try { PsiScopesUtil.setupAndRunProcessor(processor, methodCall, false); final List<Pair<PsiMethod, PsiSubstitutor>> candidates = ContainerUtil.mapNotNull( processor.getResults(), new Function<CandidateInfo, Pair<PsiMethod, PsiSubstitutor>>() { @Override public Pair<PsiMethod, PsiSubstitutor> fun(CandidateInfo info) { PsiElement element = info.getElement(); return element instanceof PsiMethod && MethodSignatureUtil.areSignaturesEqual(method, (PsiMethod)element) ? Pair.create((PsiMethod)element, info.getSubstitutor()) : null; } }); if (candidates.size() > 1) { final List<PsiClassType> ex = collectSubstituted(substitutor, thrownExceptions); for (Pair<PsiMethod, PsiSubstitutor> pair : candidates) { final PsiClassType[] exceptions = pair.first.getThrowsList().getReferencedTypes(); if (exceptions.length == 0) { return getUnhandledExceptions(methodCall, topElement, PsiSubstitutor.EMPTY, PsiClassType.EMPTY_ARRAY); } retainExceptions(ex, collectSubstituted(pair.second, exceptions)); } return getUnhandledExceptions(methodCall, topElement, PsiSubstitutor.EMPTY, ex.toArray(new PsiClassType[ex.size()])); } } catch (MethodProcessorSetupFailedException ignore) { return Collections.emptyList(); } } } return getUnhandledExceptions(method, methodCall, topElement, substitutor); } public static void retainExceptions(List<PsiClassType> ex, List<PsiClassType> thrownEx) { final List<PsiClassType> replacement = new ArrayList<PsiClassType>(); for (Iterator<PsiClassType> iterator = ex.iterator(); iterator.hasNext(); ) { PsiClassType classType = iterator.next(); boolean found = false; for (PsiClassType psiClassType : thrownEx) { if (psiClassType.isAssignableFrom(classType)) { found = true; break; } else if (classType.isAssignableFrom(psiClassType)) { replacement.add(psiClassType); iterator.remove(); found = true; break; } } if (!found) { iterator.remove(); } } ex.addAll(replacement); } public static List<PsiClassType> collectSubstituted(PsiSubstitutor substitutor, PsiClassType[] thrownExceptions) { final List<PsiClassType> ex = new ArrayList<PsiClassType>(); for (PsiClassType thrownException : thrownExceptions) { final PsiType psiType = substitutor.substitute(thrownException); if (psiType instanceof PsiClassType) { ex.add((PsiClassType)psiType); } } return ex; } @NotNull public static List<PsiClassType> getCloserExceptions(@NotNull final PsiResourceVariable resource) { final PsiMethod method = PsiUtil.getResourceCloserMethod(resource); return method != null ? getExceptionsByMethod(method, PsiSubstitutor.EMPTY) : Collections.<PsiClassType>emptyList(); } @NotNull public static List<PsiClassType> getUnhandledCloserExceptions(@NotNull 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 public static List<PsiClassType> getUnhandledExceptions(@NotNull PsiThrowStatement throwStatement, @Nullable PsiElement topElement) { List<PsiClassType> unhandled = new SmartList<PsiClassType>(); for (PsiType type : getPreciseThrowTypes(throwStatement.getException())) { List<PsiType> types = type instanceof PsiDisjunctionType ? ((PsiDisjunctionType)type).getDisjunctions() : Collections.singletonList(type); for (PsiType subType : types) { if (subType instanceof PsiClassType) { PsiClassType classType = (PsiClassType)subType; if (!isUncheckedException(classType) && !isHandled(throwStatement, classType, topElement)) { unhandled.add(classType); } } } } return unhandled; } @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(); } } if (expression != null) { final PsiType type = expression.getType(); if (type != null) { return Arrays.asList(type); } } return Collections.emptyList(); } @NotNull private static List<PsiClassType> getUnhandledExceptions(@Nullable PsiMethod method, PsiElement element, PsiElement topElement, @NotNull PsiSubstitutor substitutor) { if (method == null || isArrayClone(method, element)) { return Collections.emptyList(); } final PsiClassType[] referencedTypes = method.getThrowsList().getReferencedTypes(); return getUnhandledExceptions(element, topElement, substitutor, referencedTypes); } private static List<PsiClassType> getUnhandledExceptions(PsiElement element, PsiElement topElement, PsiSubstitutor substitutor, PsiClassType[] referencedTypes) { if (referencedTypes.length > 0) { List<PsiClassType> result = ContainerUtil.newArrayList(); for (PsiClassType referencedType : referencedTypes) { final PsiType type = GenericsUtil.eliminateWildcards(substitutor.substitute(referencedType), false); if (!(type instanceof PsiClassType)) continue; PsiClassType classType = (PsiClassType)type; PsiClass exceptionClass = ((PsiClassType)type).resolve(); if (exceptionClass == null) continue; if (isUncheckedException(classType)) continue; if (isHandled(element, classType, topElement)) continue; result.add((PsiClassType)type); } return result; } return Collections.emptyList(); } private static boolean isArrayClone(@NotNull PsiMethod method, PsiElement element) { if (!method.getName().equals(CLONE_METHOD_NAME)) return false; PsiClass containingClass = method.getContainingClass(); if (containingClass == null || !CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) { return false; } if (element instanceof PsiMethodReferenceExpression) { final PsiMethodReferenceExpression methodCallExpression = (PsiMethodReferenceExpression)element; final PsiExpression qualifierExpression = methodCallExpression.getQualifierExpression(); return qualifierExpression != null && qualifierExpression.getType() instanceof PsiArrayType; } if (!(element instanceof PsiMethodCallExpression)) return false; PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); return qualifierExpression != null && qualifierExpression.getType() instanceof PsiArrayType; } public static boolean isUncheckedException(@NotNull PsiClassType type) { return InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) || InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ERROR); } public static boolean isUncheckedExceptionOrSuperclass(@NotNull final PsiClassType type) { return isGeneralExceptionType(type) || isUncheckedException(type); } public static boolean isGeneralExceptionType(@NotNull final PsiType type) { final String canonicalText = type.getCanonicalText(); return CommonClassNames.JAVA_LANG_THROWABLE.equals(canonicalText) || CommonClassNames.JAVA_LANG_EXCEPTION.equals(canonicalText); } public static boolean isHandled(@NotNull PsiClassType exceptionType, @NotNull PsiElement throwPlace) { return isHandled(throwPlace, exceptionType, throwPlace.getContainingFile()); } private static boolean isHandled(@Nullable PsiElement element, @NotNull PsiClassType exceptionType, PsiElement topElement) { if (element == null || element.getParent() == topElement || element.getParent() == null) return false; final PsiElement parent = element.getParent(); if (parent instanceof PsiMethod) { PsiMethod method = (PsiMethod)parent; return isHandledByMethodThrowsClause(method, exceptionType); } else if (parent instanceof PsiClass) { // arguments to anon class constructor should be handled higher // like in void f() throws XXX { new AA(methodThrowingXXX()) { ... }; } return parent instanceof PsiAnonymousClass && isHandled(parent, exceptionType, topElement); } else if (parent instanceof PsiLambdaExpression) { final PsiType interfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType(); return isDeclaredBySAMMethod(exceptionType, interfaceType); } else if (element instanceof PsiMethodReferenceExpression) { final PsiType interfaceType = ((PsiMethodReferenceExpression)element).getFunctionalInterfaceType(); return isDeclaredBySAMMethod(exceptionType, interfaceType); } else if (parent instanceof PsiClassInitializer) { if (((PsiClassInitializer)parent).hasModifierProperty(PsiModifier.STATIC)) return false; // anonymous class initializers can throw any exceptions if (!(parent.getParent() instanceof PsiAnonymousClass)) { // exception thrown from within class instance initializer must be handled in every class constructor // check each constructor throws exception or superclass (there must be at least one) final PsiClass aClass = ((PsiClassInitializer)parent).getContainingClass(); return areAllConstructorsThrow(aClass, exceptionType); } } else if (parent instanceof PsiTryStatement) { PsiTryStatement tryStatement = (PsiTryStatement)parent; if (tryStatement.getTryBlock() == element && isCaught(tryStatement, exceptionType)) { return true; } if (tryStatement.getResourceList() == element && isCaught(tryStatement, exceptionType)) { return true; } PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (element instanceof PsiCatchSection && finallyBlock != null && blockCompletesAbruptly(finallyBlock)) { // exception swallowed return true; } } else if (parent instanceof JavaCodeFragment) { JavaCodeFragment codeFragment = (JavaCodeFragment)parent; JavaCodeFragment.ExceptionHandler exceptionHandler = codeFragment.getExceptionHandler(); return exceptionHandler != null && exceptionHandler.isHandledException(exceptionType); } else if (PsiImplUtil.isInServerPage(parent) && parent instanceof PsiFile) { return true; } else if (parent instanceof PsiFile) { return false; } else if (parent instanceof PsiField && ((PsiField)parent).getInitializer() == element) { final PsiClass aClass = ((PsiField)parent).getContainingClass(); if (aClass != null && !(aClass instanceof PsiAnonymousClass) && !((PsiField)parent).hasModifierProperty(PsiModifier.STATIC)) { // exceptions thrown in field initializers should be thrown in all class constructors return areAllConstructorsThrow(aClass, exceptionType); } } return isHandled(parent, exceptionType, topElement); } private static boolean isDeclaredBySAMMethod(@NotNull PsiClassType exceptionType, @Nullable PsiType interfaceType) { if (interfaceType != null) { final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(interfaceType); if (interfaceMethod != null) { return isHandledByMethodThrowsClause(interfaceMethod, exceptionType); } } return true; } private static boolean areAllConstructorsThrow(@Nullable final PsiClass aClass, @NotNull PsiClassType exceptionType) { if (aClass == null) return false; final PsiMethod[] constructors = aClass.getConstructors(); boolean thrown = constructors.length != 0; for (PsiMethod constructor : constructors) { if (!isHandledByMethodThrowsClause(constructor, exceptionType)) { thrown = false; break; } } return thrown; } private static boolean isCaught(@NotNull PsiTryStatement tryStatement, @NotNull PsiClassType exceptionType) { // if finally block completes abruptly, exception gets lost PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); if (finallyBlock != null && blockCompletesAbruptly(finallyBlock)) return true; final PsiParameter[] catchBlockParameters = tryStatement.getCatchBlockParameters(); for (PsiParameter parameter : catchBlockParameters) { PsiType paramType = parameter.getType(); if (paramType.isAssignableFrom(exceptionType)) return true; } return false; } private static boolean blockCompletesAbruptly(@NotNull final PsiCodeBlock finallyBlock) { try { ControlFlow flow = ControlFlowFactory.getInstance(finallyBlock.getProject()).getControlFlow(finallyBlock, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); int completionReasons = ControlFlowUtil.getCompletionReasons(flow, 0, flow.getSize()); if ((completionReasons & ControlFlowUtil.NORMAL_COMPLETION_REASON) == 0) return true; } catch (AnalysisCanceledException e) { return true; } return false; } private static boolean isHandledByMethodThrowsClause(@NotNull PsiMethod method, @NotNull PsiClassType exceptionType) { final PsiClassType[] referencedTypes = method.getThrowsList().getReferencedTypes(); return isHandledBy(exceptionType, referencedTypes); } public static boolean isHandledBy(@NotNull PsiClassType exceptionType, @NotNull PsiClassType[] referencedTypes) { for (PsiClassType classType : referencedTypes) { if (classType.isAssignableFrom(exceptionType)) return true; } return false; } public static void sortExceptionsByHierarchy(@NotNull List<PsiClassType> exceptions) { if (exceptions.size() <= 1) return; sortExceptionsByHierarchy(exceptions.subList(1, exceptions.size())); for (int i=0; i<exceptions.size()-1;i++) { if (TypeConversionUtil.isAssignable(exceptions.get(i), exceptions.get(i+1))) { Collections.swap(exceptions, i,i+1); } } } }