List<PsiType> (@Nullable final PsiParameter parameter) { if (parameter == null) { return ContainerUtil.emptyList(); } PsiType declaredType = parameter.getType(); // 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())) { PsiTryStatement statement = getTryStatement(); // ... and the try block of the try statement which declares Cj (tryBlock) can throw T ... Collection<PsiClassType> thrownTypes = getThrownTypes(statement); if (thrownTypes.isEmpty()) return Collections.emptyList(); // ... 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 = statement.getCatchBlockParameters(); final int currentIdx = ArrayUtil.find(parameters, parameter); List<PsiType> uncaughtTypes = ContainerUtil.mapNotNull(thrownTypes, thrownType -> { for (int i = 0; i < currentIdx; i++) { final PsiType catchType = parameters[i].getType(); if (catchType.isAssignableFrom(thrownType)) return null; } return thrownType; }); if (uncaughtTypes.isEmpty()) return Collections.emptyList(); // unreachable catch section // ... and T is assignable to Ej ... if (declaredType instanceof PsiDisjunctionType) { return Collections.unmodifiableList(((PsiDisjunctionType)declaredType).getDisjunctions() .stream() .flatMap(disjunction -> computePreciseCatchTypes(disjunction, uncaughtTypes).stream()) .collect(Collectors.toCollection(() -> new ArrayList<>()))); } else { return computePreciseCatchTypes(declaredType, uncaughtTypes); } } return Collections.singletonList(declaredType); }