2 * Copyright 2003-2016 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.codeInspection.ui.SingleCheckboxOptionsPanel;
20 import com.intellij.psi.*;
21 import com.intellij.psi.util.PsiTreeUtil;
22 import com.siyeh.InspectionGadgetsBundle;
23 import com.siyeh.ig.BaseInspection;
24 import com.siyeh.ig.BaseInspectionVisitor;
25 import com.siyeh.ig.psiutils.ExpressionUtils;
26 import com.siyeh.ig.psiutils.ParenthesesUtils;
27 import com.siyeh.ig.psiutils.VariableAccessUtils;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
32 import java.util.List;
34 public class ThrowFromFinallyBlockInspection extends BaseInspection {
36 @SuppressWarnings("PublicField")
37 public boolean warnOnAllExceptions = false;
41 public String getDisplayName() {
42 return InspectionGadgetsBundle.message(
43 "throw.from.finally.block.display.name");
47 public boolean isEnabledByDefault() {
53 public JComponent createOptionsPanel() {
54 return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("throw,from.finally.block.everywhere.option"),
55 this, "warnOnAllExceptions");
60 protected String buildErrorString(Object... infos) {
61 if (infos.length == 0) {
62 return InspectionGadgetsBundle.message("throw.from.finally.block.problem.descriptor");
65 final PsiClassType type = (PsiClassType)infos[0];
66 return InspectionGadgetsBundle.message("possible.throw.from.finally.block.problem.descriptor", type.getPresentableText());
71 public BaseInspectionVisitor buildVisitor() {
72 return new ThrowFromFinallyBlockVisitor();
75 private class ThrowFromFinallyBlockVisitor extends BaseInspectionVisitor {
78 public void visitCallExpression(PsiCallExpression expression) {
79 super.visitCallExpression(expression);
80 if (!warnOnAllExceptions) {
83 final List<PsiClassType> exceptions = ExceptionUtil.getThrownExceptions(expression);
84 if (exceptions.isEmpty()) {
87 for (PsiClassType exception : exceptions) {
88 final PsiCodeBlock finallyBlock = getContainingFinallyBlock(expression, exception);
89 if (finallyBlock != null && isHidingOfPreviousException(finallyBlock, expression)) {
90 if (expression instanceof PsiMethodCallExpression) {
91 registerMethodCallError((PsiMethodCallExpression)expression, exception);
93 else if (expression instanceof PsiNewExpression) {
94 registerNewExpressionError((PsiNewExpression)expression, exception);
102 public void visitThrowStatement(PsiThrowStatement statement) {
103 super.visitThrowStatement(statement);
104 final PsiExpression exception = ParenthesesUtils.stripParentheses(statement.getException());
105 if (exception == null) {
108 final PsiType type = exception.getType();
112 final PsiCodeBlock finallyBlock = getContainingFinallyBlock(statement, type);
113 if (finallyBlock == null) {
116 if (exception instanceof PsiReferenceExpression) {
117 final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)exception;
118 final PsiElement target = referenceExpression.resolve();
119 if (target == null || !PsiTreeUtil.isAncestor(finallyBlock, target, true)) {
120 // variable from outside finally block is thrown
124 if (isHidingOfPreviousException(finallyBlock, statement)) {
125 registerStatementError(statement);
129 private boolean isHidingOfPreviousException(PsiCodeBlock finallyBlock, PsiElement throwElement) {
130 final PsiElement parent = finallyBlock.getParent();
131 if (!(parent instanceof PsiTryStatement)) {
135 final PsiTryStatement tryStatement = (PsiTryStatement)parent;
136 final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
137 if (catchBlocks.length == 0) {
140 final PsiIfStatement ifStatement = getParentOfType(throwElement, PsiIfStatement.class, finallyBlock);
141 if (ifStatement == null) {
144 final boolean inThenBranch = PsiTreeUtil.isAncestor(ifStatement.getThenBranch(), throwElement, false);
145 final boolean inElseBranch = PsiTreeUtil.isAncestor(ifStatement.getElseBranch(), throwElement, false);
146 if (!inThenBranch && !inElseBranch) {
149 final PsiExpression condition = ifStatement.getCondition();
150 final PsiVariable variable = ExpressionUtils.getVariableFromNullComparison(condition, inThenBranch);
151 if (variable == null) {
154 boolean assigned = true;
155 for (PsiCodeBlock catchBlock : catchBlocks) {
156 assigned &= VariableAccessUtils.variableIsAssigned(variable, catchBlock);
162 public <T extends PsiElement> T getParentOfType(@Nullable PsiElement element, @NotNull Class<T> aClass, @NotNull PsiElement stopAt) {
163 if (element == null || element instanceof PsiFile) return null;
164 element = element.getParent();
166 while (element != null && !aClass.isInstance(element)) {
167 if (element == stopAt || element instanceof PsiFile) return null;
168 element = element.getParent();
170 //noinspection unchecked
175 private static PsiCodeBlock getContainingFinallyBlock(@NotNull PsiElement element, @NotNull PsiType thrownType) {
176 PsiElement currentElement = element;
178 final PsiTryStatement tryStatement = PsiTreeUtil
179 .getParentOfType(currentElement, PsiTryStatement.class, true, PsiClass.class, PsiLambdaExpression.class);
180 if (tryStatement == null) {
183 final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
184 if (PsiTreeUtil.isAncestor(finallyBlock, currentElement, true)) {
187 if (PsiTreeUtil.isAncestor(tryStatement.getTryBlock(), currentElement, true) && isCaught(tryStatement, thrownType)) {
190 currentElement = tryStatement;
194 private static boolean isCaught(PsiTryStatement tryStatement, PsiType exceptionType) {
195 for (PsiParameter parameter : tryStatement.getCatchBlockParameters()) {
196 final PsiType type = parameter.getType();
197 if (type.isAssignableFrom(exceptionType)) return true;