IDEADEV-30513 ("Remove try-catch statement" reorders the statements wrongly)
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / errorhandling / CaughtExceptionImmediatelyRethrownInspection.java
blob967a4c334dcdeac5edb82a79bd93501e5f8f28c7
1 /*
2 * Copyright 2007-2008 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.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.*;
21 import com.intellij.psi.search.searches.ReferencesSearch;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.util.IncorrectOperationException;
24 import com.intellij.util.Query;
25 import com.siyeh.InspectionGadgetsBundle;
26 import com.siyeh.ig.BaseInspection;
27 import com.siyeh.ig.BaseInspectionVisitor;
28 import com.siyeh.ig.InspectionGadgetsFix;
29 import com.siyeh.ig.psiutils.VariableSearchUtils;
30 import org.jetbrains.annotations.Nls;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
34 public class CaughtExceptionImmediatelyRethrownInspection
35 extends BaseInspection {
37 @Override
38 @Nls
39 @NotNull
40 public String getDisplayName() {
41 return InspectionGadgetsBundle.message(
42 "caught.exception.immediately.rethrown.display.name");
45 @Override
46 @NotNull
47 protected String buildErrorString(Object... infos) {
48 return InspectionGadgetsBundle.message(
49 "caught.exception.immediately.rethrown.problem.descriptor");
52 @Override
53 public boolean isEnabledByDefault() {
54 return true;
57 @Override
58 @Nullable
59 protected InspectionGadgetsFix buildFix(Object... infos) {
60 final PsiTryStatement tryStatement = (PsiTryStatement) infos[0];
61 final boolean removeTryCatch =
62 tryStatement.getCatchSections().length == 1 &&
63 tryStatement.getFinallyBlock() == null;
64 return new DeleteCatchSectionFix(removeTryCatch);
67 private static class DeleteCatchSectionFix extends InspectionGadgetsFix {
69 private final boolean removeTryCatch;
71 DeleteCatchSectionFix(boolean removeTryCatch) {
72 this.removeTryCatch = removeTryCatch;
75 @NotNull
76 public String getName() {
77 if (removeTryCatch) {
78 return InspectionGadgetsBundle.message(
79 "remove.try.catch.quickfix");
80 } else {
81 return InspectionGadgetsBundle.message(
82 "delete.catch.section.quickfix");
86 @Override
87 protected void doFix(Project project, ProblemDescriptor descriptor)
88 throws IncorrectOperationException {
89 final PsiElement element = descriptor.getPsiElement();
90 final PsiElement parent = element.getParent();
91 if (!(parent instanceof PsiParameter)) {
92 return;
94 final PsiParameter parameter = (PsiParameter)parent;
95 final PsiElement grandParent = parameter.getParent();
96 if (!(grandParent instanceof PsiCatchSection)) {
97 return;
99 final PsiCatchSection catchSection = (PsiCatchSection)grandParent;
100 final PsiTryStatement tryStatement = catchSection.getTryStatement();
101 final boolean removeTryCatch =
102 tryStatement.getCatchSections().length == 1 &&
103 tryStatement.getFinallyBlock() == null;
104 if (removeTryCatch) {
105 final PsiCodeBlock codeBlock = tryStatement.getTryBlock();
106 if (codeBlock == null) {
107 return;
109 final PsiStatement[] statements = codeBlock.getStatements();
110 if (statements.length == 0) {
111 tryStatement.delete();
113 final PsiElement containingElement = tryStatement.getParent();
114 final boolean keepBlock;
115 if (containingElement instanceof PsiCodeBlock) {
116 final PsiCodeBlock parentBlock =
117 (PsiCodeBlock)containingElement;
118 keepBlock =
119 VariableSearchUtils.containsConflictingDeclarations(
120 codeBlock, parentBlock);
121 } else {
122 keepBlock = true;
124 if (keepBlock) {
125 final JavaPsiFacade psiFacade =
126 JavaPsiFacade.getInstance(project);
127 final PsiElementFactory factory =
128 psiFacade.getElementFactory();
129 final PsiBlockStatement resultStatement = (PsiBlockStatement)
130 factory.createStatementFromText("{}", element);
131 final PsiCodeBlock resultBlock =
132 resultStatement.getCodeBlock();
133 for (PsiStatement statement : statements) {
134 resultBlock.add(statement);
136 tryStatement.replace(resultStatement);
137 } else {
138 for (PsiStatement statement : statements) {
139 containingElement.addBefore(statement, tryStatement);
141 tryStatement.delete();
143 } else {
144 catchSection.delete();
149 @Override
150 public BaseInspectionVisitor buildVisitor() {
151 return new CaughtExceptionImmediatelyRethrownVisitor();
154 private static class CaughtExceptionImmediatelyRethrownVisitor
155 extends BaseInspectionVisitor {
157 @Override
158 public void visitThrowStatement(PsiThrowStatement statement) {
159 super.visitThrowStatement(statement);
160 final PsiExpression expression = statement.getException();
161 if (!(expression instanceof PsiReferenceExpression)) {
162 return;
164 final PsiStatement previousStatement =
165 PsiTreeUtil.getPrevSiblingOfType(statement,
166 PsiStatement.class);
167 if (previousStatement != null) {
168 return;
170 final PsiElement parent = statement.getParent();
171 if (parent instanceof PsiStatement) {
172 // e.g. if (notsure) throw e;
173 return;
175 final PsiReferenceExpression referenceExpression =
176 (PsiReferenceExpression)expression;
177 final PsiElement target = referenceExpression.resolve();
178 if (!(target instanceof PsiParameter)) {
179 return;
181 final PsiParameter parameter = (PsiParameter)target;
182 final PsiElement declarationScope = parameter.getDeclarationScope();
183 if (!(declarationScope instanceof PsiCatchSection)) {
184 return;
186 final PsiCatchSection catchSection =
187 (PsiCatchSection)declarationScope;
188 final PsiCodeBlock block =
189 PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class);
190 if (block == null) {
191 return;
193 final PsiElement blockParent = block.getParent();
194 if (blockParent != catchSection) {
195 // e.g. if (notsure) { throw e; }
196 return;
198 if (isSuperClassExceptionCaughtLater(parameter, catchSection)) {
199 return;
201 final Query<PsiReference> query =
202 ReferencesSearch.search(parameter);
203 for (PsiReference reference : query) {
204 final PsiElement element = reference.getElement();
205 if (element != expression) {
206 return;
209 final PsiTryStatement tryStatement = catchSection.getTryStatement();
210 registerVariableError(parameter, tryStatement);
213 private static boolean isSuperClassExceptionCaughtLater(
214 PsiVariable parameter, PsiCatchSection catchSection) {
215 final PsiTryStatement tryStatement = catchSection.getTryStatement();
216 final PsiCatchSection[] catchSections =
217 tryStatement.getCatchSections();
218 int index = 0;
219 while (catchSections[index] != catchSection &&
220 index < catchSections.length) {
221 index++;
223 final PsiType type = parameter.getType();
224 if (!(type instanceof PsiClassType)) {
225 return false;
227 final PsiClassType classType = (PsiClassType)type;
228 final PsiClass parameterClass = classType.resolve();
229 if (parameterClass == null) {
230 return false;
232 for (int i = index; i < catchSections.length; i++){
233 final PsiCatchSection nextCatchSection = catchSections[i];
234 final PsiParameter nextParameter =
235 nextCatchSection.getParameter();
236 if (nextParameter == null) {
237 continue;
239 final PsiType nextType = nextParameter.getType();
240 if (!(nextType instanceof PsiClassType)) {
241 continue;
243 final PsiClassType nextClassType = (PsiClassType)nextType;
244 final PsiClass aClass = nextClassType.resolve();
245 if (aClass == null) {
246 continue;
248 if (parameterClass.isInheritor(aClass, true)) {
249 return true;
252 return false;