8fcbd5482d116d90641df74442b7177b982bb576
[idea/community.git] / plugins / InspectionGadgets / InspectionGadgetsAnalysis / src / com / siyeh / ig / bugs / ThrowableResultOfMethodCallIgnoredInspection.java
1 /*
2  * Copyright 2008-2015 Bas Leijdekkers
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.siyeh.ig.bugs;
17
18 import com.intellij.psi.*;
19 import com.intellij.psi.search.searches.ReferencesSearch;
20 import com.intellij.psi.util.InheritanceUtil;
21 import com.intellij.psi.util.PropertyUtil;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.util.Query;
24 import com.siyeh.InspectionGadgetsBundle;
25 import com.siyeh.ig.BaseInspection;
26 import com.siyeh.ig.BaseInspectionVisitor;
27 import com.siyeh.ig.psiutils.MethodUtils;
28 import com.siyeh.ig.psiutils.TypeUtils;
29 import org.jetbrains.annotations.NotNull;
30
31 public class ThrowableResultOfMethodCallIgnoredInspection extends BaseInspection {
32
33   @Override
34   @NotNull
35   public String getDisplayName() {
36     return InspectionGadgetsBundle.message(
37       "throwable.result.of.method.call.ignored.display.name");
38   }
39
40   @Override
41   @NotNull
42   protected String buildErrorString(Object... infos) {
43     return InspectionGadgetsBundle.message(
44       "throwable.result.of.method.call.ignored.problem.descriptor");
45   }
46
47   @Override
48   public boolean isEnabledByDefault() {
49     return true;
50   }
51
52   @Override
53   public BaseInspectionVisitor buildVisitor() {
54     return new ThrowableResultOfMethodCallIgnoredVisitor();
55   }
56
57   private static class ThrowableResultOfMethodCallIgnoredVisitor extends BaseInspectionVisitor {
58
59     @Override
60     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
61       super.visitMethodCallExpression(expression);
62       PsiElement parent = expression.getParent();
63       while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiTypeCastExpression) {
64         parent = parent.getParent();
65       }
66       if (canBeThrown(parent)) {
67         return;
68       }
69       if (!TypeUtils.expressionHasTypeOrSubtype(expression, CommonClassNames.JAVA_LANG_THROWABLE)) {
70         return;
71       }
72       final PsiMethod method = expression.resolveMethod();
73       if (method == null || PropertyUtil.isSimpleGetter(method)) {
74         return;
75       }
76       final PsiClass containingClass = method.getContainingClass();
77       if (containingClass == null) {
78         return;
79       }
80       if (!method.hasModifierProperty(PsiModifier.STATIC) &&
81           InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_LANG_THROWABLE)) {
82         return;
83       }
84       if ("propagate".equals(method.getName()) && "com.google.common.base.Throwables".equals(containingClass.getQualifiedName())) {
85         return;
86       }
87       final PsiElement var = getVariable(parent, expression);
88       if (var == null) {
89         return;
90       }
91
92       if (var instanceof PsiLocalVariable) {
93         final Query<PsiReference> query = ReferencesSearch.search(var, var.getUseScope());
94         for (PsiReference reference : query) {
95           final PsiElement usage = reference.getElement();
96           PsiElement usageParent = usage.getParent();
97           while (usageParent instanceof PsiParenthesizedExpression) {
98             usageParent = usageParent.getParent();
99           }
100           if (canBeThrown(usageParent)) {
101             return;
102           }
103         }
104       }
105       registerMethodCallError(expression);
106     }
107
108     private static boolean canBeThrown(PsiElement parent) {
109       if (parent instanceof PsiReturnStatement ||
110           parent instanceof PsiThrowStatement ||
111           parent instanceof PsiExpressionList ||
112           parent instanceof PsiLambdaExpression) {
113         return true;
114       }
115       final PsiElement grandParent = parent.getParent();
116       if (grandParent instanceof PsiMethodCallExpression) {
117         final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent;
118         final PsiMethod method = methodCallExpression.resolveMethod();
119         if (MethodUtils.isChainable(method)) {
120           return true;
121         }
122       }
123       return false;
124     }
125   }
126
127   protected static PsiElement getVariable(PsiElement parent, PsiElement expression) {
128     if (parent instanceof PsiAssignmentExpression) {
129       final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
130       final PsiExpression rhs = assignmentExpression.getRExpression();
131       if (!PsiTreeUtil.isAncestor(rhs, expression, false)) {
132         return null;
133       }
134       final PsiExpression lhs = assignmentExpression.getLExpression();
135       if (!(lhs instanceof PsiReferenceExpression)) {
136         return null;
137       }
138       final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs;
139       final PsiElement target = referenceExpression.resolve();
140       if (!(target instanceof PsiLocalVariable)) {
141         return null;
142       }
143       return target;
144     }
145     else {
146       return parent;
147     }
148   }
149 }