2 * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.siyeh.ig.errorhandling;
18 import com.intellij.codeInsight.ExceptionUtil;
19 import com.intellij.psi.*;
20 import com.intellij.psi.util.PsiTreeUtil;
21 import com.siyeh.InspectionGadgetsBundle;
22 import com.siyeh.ig.BaseInspection;
23 import com.siyeh.ig.BaseInspectionVisitor;
24 import com.siyeh.ig.psiutils.ExpressionUtils;
25 import com.siyeh.ig.psiutils.ParenthesesUtils;
26 import com.siyeh.ig.psiutils.VariableAccessUtils;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.util.List;
35 public class ThrowFromFinallyBlockInspection extends BaseInspection {
39 public String getDisplayName() {
40 return InspectionGadgetsBundle.message(
41 "throw.from.finally.block.display.name");
45 public boolean isEnabledByDefault() {
51 protected String buildErrorString(Object... infos) {
52 if (infos.length == 0) {
53 return InspectionGadgetsBundle.message("throw.from.finally.block.problem.descriptor");
56 final PsiClassType type = (PsiClassType)infos[0];
57 return InspectionGadgetsBundle.message("possible.throw.from.finally.block.problem.descriptor", type.getPresentableText());
62 public BaseInspectionVisitor buildVisitor() {
63 return new ThrowFromFinallyBlockVisitor();
66 private static class ThrowFromFinallyBlockVisitor extends BaseInspectionVisitor {
69 public void visitMethodCallExpression(PsiMethodCallExpression expression) {
70 super.visitMethodCallExpression(expression);
71 final List<PsiClassType> exceptions = ExceptionUtil.getThrownExceptions(expression);
72 if (exceptions.isEmpty()) {
75 for (PsiClassType exception : exceptions) {
76 final PsiCodeBlock finallyBlock = getContainingFinallyBlock(expression, exception);
77 if (finallyBlock != null && isHidingOfPreviousException(finallyBlock, expression)) {
78 registerMethodCallError(expression, exception);
85 public void visitThrowStatement(PsiThrowStatement statement) {
86 super.visitThrowStatement(statement);
87 final PsiExpression exception = ParenthesesUtils.stripParentheses(statement.getException());
88 if (exception == null) {
91 final PsiType type = exception.getType();
95 final PsiCodeBlock finallyBlock = getContainingFinallyBlock(statement, type);
96 if (finallyBlock == null) {
99 if (exception instanceof PsiReferenceExpression) {
100 final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)exception;
101 final PsiElement target = referenceExpression.resolve();
102 if (target == null || !PsiTreeUtil.isAncestor(finallyBlock, target, true)) {
103 // variable from outside finally block is thrown
107 if (isHidingOfPreviousException(finallyBlock, statement)) {
108 registerStatementError(statement);
112 private static boolean isHidingOfPreviousException(PsiCodeBlock finallyBlock, PsiElement throwElement) {
113 final PsiElement parent = finallyBlock.getParent();
114 if (!(parent instanceof PsiTryStatement)) {
118 final PsiTryStatement tryStatement = (PsiTryStatement)parent;
119 final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
120 if (catchBlocks.length == 0) {
123 final PsiIfStatement ifStatement = getParentOfType(throwElement, PsiIfStatement.class, finallyBlock);
124 if (ifStatement == null) {
127 final boolean inThenBranch = PsiTreeUtil.isAncestor(ifStatement.getThenBranch(), throwElement, false);
128 final boolean inElseBranch = PsiTreeUtil.isAncestor(ifStatement.getElseBranch(), throwElement, false);
129 if (!inThenBranch && !inElseBranch) {
132 final PsiExpression condition = ifStatement.getCondition();
133 final PsiVariable variable = ExpressionUtils.getVariableFromNullComparison(condition, inThenBranch);
134 if (variable == null) {
137 boolean assigned = true;
138 for (PsiCodeBlock catchBlock : catchBlocks) {
139 assigned &= VariableAccessUtils.variableIsAssigned(variable, catchBlock);
145 public static <T extends PsiElement> T getParentOfType(@Nullable PsiElement element,
146 @NotNull Class<T> aClass,
147 @NotNull PsiElement stopAt) {
148 if (element == null || element instanceof PsiFile) return null;
149 element = element.getParent();
151 while (element != null && !aClass.isInstance(element)) {
152 if (element == stopAt || element instanceof PsiFile) return null;
153 element = element.getParent();
155 //noinspection unchecked
160 private static PsiCodeBlock getContainingFinallyBlock(@NotNull PsiElement element, @NotNull PsiType thrownType) {
161 PsiElement currentElement = element;
163 final PsiTryStatement tryStatement = PsiTreeUtil
164 .getParentOfType(currentElement, PsiTryStatement.class, true, PsiClass.class, PsiLambdaExpression.class);
165 if (tryStatement == null) {
168 final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
169 if (PsiTreeUtil.isAncestor(finallyBlock, currentElement, true)) {
172 if (PsiTreeUtil.isAncestor(tryStatement.getTryBlock(), currentElement, true) && isCaught(tryStatement, thrownType)) {
175 currentElement = tryStatement;
179 private static boolean isCaught(PsiTryStatement tryStatement, PsiType exceptionType) {
180 for (PsiParameter parameter : tryStatement.getCatchBlockParameters()) {
181 final PsiType type = parameter.getType();
182 if (type.isAssignableFrom(exceptionType)) return true;