remove redundant casts when extract changed parameter type accordingly (IDEA-79743)
[idea/community.git] / java / java-impl / src / com / intellij / codeInspection / redundantCast / RedundantCastInspection.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
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.intellij.codeInspection.redundantCast;
17
18 import com.intellij.codeInsight.CodeInsightUtilBase;
19 import com.intellij.codeInsight.NullableNotNullManager;
20 import com.intellij.codeInsight.daemon.GroupNames;
21 import com.intellij.codeInspection.*;
22 import com.intellij.codeInspection.miscGenerics.GenericsInspectionToolBase;
23 import com.intellij.codeInspection.miscGenerics.SuspiciousCollectionsMethodCallsInspection;
24 import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.WriteExternalException;
28 import com.intellij.psi.*;
29 import com.intellij.psi.util.PsiUtil;
30 import com.intellij.psi.util.RedundantCastUtil;
31 import com.intellij.util.containers.IntArrayList;
32 import org.jdom.Element;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import javax.swing.*;
38 import java.util.ArrayList;
39 import java.util.List;
40
41 /**
42  * @author max
43  * Date: Dec 24, 2001
44  */
45 public class RedundantCastInspection extends GenericsInspectionToolBase {
46   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastInspection");
47   private final LocalQuickFix myQuickFixAction;
48   private static final String DISPLAY_NAME = InspectionsBundle.message("inspection.redundant.cast.display.name");
49   @NonNls private static final String SHORT_NAME = "RedundantCast";
50
51   public boolean IGNORE_ANNOTATED_METHODS = false;
52   public boolean IGNORE_SUSPICIOUS_METHOD_CALLS = false;
53
54
55   public RedundantCastInspection() {
56     myQuickFixAction = new AcceptSuggested();
57   }
58
59   @Nullable
60   public ProblemDescriptor[] getDescriptions(PsiElement where, InspectionManager manager, boolean isOnTheFly) {
61     List<PsiTypeCastExpression> redundantCasts = RedundantCastUtil.getRedundantCastsInside(where);
62     if (redundantCasts.isEmpty()) return null;
63     List<ProblemDescriptor> descriptions = new ArrayList<ProblemDescriptor>(redundantCasts.size());
64     for (PsiTypeCastExpression redundantCast : redundantCasts) {
65       ProblemDescriptor descriptor = createDescription(redundantCast, manager, isOnTheFly);
66       if (descriptor != null) {
67         descriptions.add(descriptor);
68       }
69     }
70     if (descriptions.isEmpty()) return null;
71     return descriptions.toArray(new ProblemDescriptor[descriptions.size()]);
72   }
73
74   @Override
75   public void writeSettings(Element node) throws WriteExternalException {
76     if (IGNORE_ANNOTATED_METHODS || IGNORE_SUSPICIOUS_METHOD_CALLS) {
77       super.writeSettings(node);
78     }
79   }
80
81   @Override
82   public JComponent createOptionsPanel() {
83     final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this);
84     optionsPanel.addCheckbox("Ignore casts appeared in suspicious collections method calls", "IGNORE_SUSPICIOUS_METHOD_CALLS");
85     optionsPanel.addCheckbox("Ignore casts to invoke @NotNull method which overrides @Nullable", "IGNORE_ANNOTATED_METHODS");
86     return optionsPanel;
87   }
88
89   @Nullable
90   private ProblemDescriptor createDescription(@NotNull PsiTypeCastExpression cast, @NotNull InspectionManager manager, boolean onTheFly) {
91     PsiExpression operand = cast.getOperand();
92     PsiTypeElement castType = cast.getCastType();
93     if (operand == null || castType == null) return null;
94     PsiElement parent = cast.getParent();
95     while (parent instanceof PsiParenthesizedExpression){
96       parent = parent.getParent();
97     }
98     if (parent instanceof PsiReferenceExpression) {
99       if (IGNORE_ANNOTATED_METHODS) {
100         final PsiElement gParent = parent.getParent();
101         if (gParent instanceof PsiMethodCallExpression) {
102           final PsiMethod psiMethod = ((PsiMethodCallExpression)gParent).resolveMethod();
103           if (psiMethod != null && NullableNotNullManager.isNotNull(psiMethod)) {
104             final PsiClass superClass = PsiUtil.resolveClassInType(operand.getType());
105             for (PsiMethod method : psiMethod.findSuperMethods(superClass)) {
106               if (NullableNotNullManager.isNullable(method)) {
107                 return null;
108               }
109             }
110           }
111         }
112       }
113     } else if (parent instanceof PsiExpressionList)  {
114       final PsiElement gParent = parent.getParent();
115       if (gParent instanceof PsiMethodCallExpression && IGNORE_SUSPICIOUS_METHOD_CALLS) {
116         final String message = SuspiciousCollectionsMethodCallsInspection
117           .getSuspiciousMethodCallMessage((PsiMethodCallExpression)gParent, operand.getType(), true, new ArrayList<PsiMethod>(), new IntArrayList());
118         if (message != null) {
119           return null;
120         }
121       }
122     }
123
124     String message = InspectionsBundle.message("inspection.redundant.cast.problem.descriptor",
125                                                "<code>" + operand.getText() + "</code>", "<code>#ref</code> #loc");
126     return manager.createProblemDescriptor(castType, message, myQuickFixAction, ProblemHighlightType.LIKE_UNUSED_SYMBOL, onTheFly);
127   }
128
129
130   private static class AcceptSuggested implements LocalQuickFix {
131     @NotNull
132     public String getName() {
133       return InspectionsBundle.message("inspection.redundant.cast.remove.quickfix");
134     }
135
136     public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
137       if (!CodeInsightUtilBase.preparePsiElementForWrite(descriptor.getPsiElement())) return;
138       PsiElement castTypeElement = descriptor.getPsiElement();
139       PsiTypeCastExpression cast = castTypeElement == null ? null : (PsiTypeCastExpression)castTypeElement.getParent();
140       if (cast != null) {
141         RedundantCastUtil.removeCast(cast);
142       }
143     }
144
145     @NotNull
146     public String getFamilyName() {
147       return getName();
148     }
149   }
150
151   @NotNull
152   public String getDisplayName() {
153     return DISPLAY_NAME;
154   }
155
156   @NotNull
157   public String getGroupDisplayName() {
158     return GroupNames.VERBOSE_GROUP_NAME;
159   }
160
161   @NotNull
162   public String getShortName() {
163     return SHORT_NAME;
164   }
165 }