SameParameterValueInspection: local inspection shouldn't inspect non-source content
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / sameParameterValue / SameParameterValueInspectionBase.java
1 /*
2  * Copyright 2000-2014 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.sameParameterValue;
17
18 import com.intellij.analysis.AnalysisScope;
19 import com.intellij.codeInsight.daemon.GroupNames;
20 import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil;
21 import com.intellij.codeInspection.*;
22 import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
23 import com.intellij.codeInspection.reference.*;
24 import com.intellij.openapi.progress.EmptyProgressIndicator;
25 import com.intellij.openapi.roots.ProjectRootManager;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
28 import com.intellij.psi.*;
29 import com.intellij.util.ObjectUtils;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import java.util.ArrayList;
34 import java.util.List;
35
36 /**
37  * @author max
38  */
39 public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionTool {
40   @Override
41   @Nullable
42   public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity,
43                                                 @NotNull AnalysisScope scope,
44                                                 @NotNull InspectionManager manager,
45                                                 @NotNull GlobalInspectionContext globalContext,
46                                                 @NotNull ProblemDescriptionsProcessor processor) {
47     List<ProblemDescriptor> problems = null;
48     if (refEntity instanceof RefMethod) {
49       final RefMethod refMethod = (RefMethod)refEntity;
50
51       if (refMethod.hasSuperMethods()) return null;
52
53       if (refMethod.isEntry()) return null;
54
55       RefParameter[] parameters = refMethod.getParameters();
56       for (RefParameter refParameter : parameters) {
57         String value = refParameter.getActualValueIfSame();
58         if (value != null) {
59           if (!globalContext.shouldCheck(refParameter, this)) continue;
60           if (problems == null) problems = new ArrayList<>(1);
61           problems.add(registerProblem(manager, refParameter.getElement(), value));
62         }
63       }
64     }
65
66     return problems == null ? null : problems.toArray(new CommonProblemDescriptor[problems.size()]);
67   }
68
69   @Override
70   protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext,
71                                                 @NotNull final ProblemDescriptionsProcessor processor) {
72     manager.iterate(new RefJavaVisitor() {
73       @Override public void visitElement(@NotNull RefEntity refEntity) {
74         if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) {
75           refEntity.accept(new RefJavaVisitor() {
76             @Override public void visitMethod(@NotNull final RefMethod refMethod) {
77               globalContext.enqueueMethodUsagesProcessor(refMethod, new GlobalJavaInspectionContext.UsagesProcessor() {
78                 @Override
79                 public boolean process(PsiReference psiReference) {
80                   processor.ignoreElement(refMethod);
81                   return false;
82                 }
83               });
84             }
85           });
86         }
87       }
88     });
89
90     return false;
91   }
92
93   @Override
94   @NotNull
95   public String getDisplayName() {
96     return InspectionsBundle.message("inspection.same.parameter.display.name");
97   }
98
99   @Override
100   @NotNull
101   public String getGroupDisplayName() {
102     return GroupNames.DECLARATION_REDUNDANCY;
103   }
104
105   @Override
106   @NotNull
107   public String getShortName() {
108     return "SameParameterValue";
109   }
110
111   @Override
112   @Nullable
113   public QuickFix getQuickFix(final String hint) {
114     if (hint == null) return null;
115     final int spaceIdx = hint.indexOf(' ');
116     if (spaceIdx == -1 || spaceIdx >= hint.length() - 1) return null; //invalid hint
117     final String paramName = hint.substring(0, spaceIdx);
118     final String value = hint.substring(spaceIdx + 1);
119     return createFix(paramName, value);
120   }
121
122   protected LocalQuickFix createFix(String paramName, String value) {
123     return null;
124   }
125
126   @Override
127   @Nullable
128   public String getHint(@NotNull final QuickFix fix) {
129     return fix.toString();
130   }
131
132   @Nullable
133   @Override
134   public LocalInspectionTool getSharedLocalInspectionTool() {
135     return new LocalSameParameterValueInspection(this);
136   }
137
138   private class LocalSameParameterValueInspection extends BaseJavaLocalInspectionTool {
139     private static final String NOT_CONST = "_NOT_CONST";
140
141     private final SameParameterValueInspectionBase myGlobal;
142
143     private LocalSameParameterValueInspection(SameParameterValueInspectionBase global) {
144       myGlobal = global;
145     }
146
147     @Override
148     @NotNull
149     public String getGroupDisplayName() {
150       return myGlobal.getGroupDisplayName();
151     }
152
153     @Override
154     @NotNull
155     public String getDisplayName() {
156       return myGlobal.getDisplayName();
157     }
158
159     @Override
160     @NotNull
161     public String getShortName() {
162       return myGlobal.getShortName();
163     }
164
165     @NotNull
166     @Override
167     public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
168                                           boolean isOnTheFly,
169                                           @NotNull LocalInspectionToolSession session) {
170       return new JavaElementVisitor() {
171         private final UnusedDeclarationInspectionBase myDeadCodeTool;
172
173         {
174           InspectionProfile profile = InspectionProjectProfileManager.getInstance(holder.getProject()).getCurrentProfile();
175           UnusedDeclarationInspectionBase deadCodeTool = (UnusedDeclarationInspectionBase)profile.getUnwrappedTool(UnusedDeclarationInspectionBase.SHORT_NAME, holder.getFile());
176           myDeadCodeTool = deadCodeTool == null ? new UnusedDeclarationInspectionBase() : deadCodeTool;
177         }
178
179         @Override
180         public void visitMethod(PsiMethod method) {
181           if (method.isConstructor()) return;
182           PsiParameter[] parameters = method.getParameterList().getParameters();
183           if (parameters.length == 0) return;
184           final VirtualFile file = method.getContainingFile().getVirtualFile();
185           if (!ProjectRootManager.getInstance(holder.getProject()).getFileIndex().isInSourceContent(file)) return;
186
187           if (myDeadCodeTool.isEntryPoint(method)) return;
188           if (!method.getHierarchicalMethodSignature().getSuperSignatures().isEmpty()) return;
189
190           PsiParameter lastParameter = parameters[parameters.length - 1];
191           final String[] paramValues;
192           final boolean hasVarArg = lastParameter.getType() instanceof PsiEllipsisType;
193           if (hasVarArg) {
194             if (parameters.length == 1) return;
195             paramValues = new String[parameters.length - 1];
196           } else {
197             paramValues = new String[parameters.length];
198           }
199
200           if (UnusedSymbolUtil.processUsages(holder.getProject(), method.getContainingFile(), method, new EmptyProgressIndicator(), null, info -> {
201             PsiElement element = info.getElement();
202
203             if (!(element instanceof PsiReferenceExpression)) {
204               return false;
205             }
206             PsiElement parent = element.getParent();
207             if (!(parent instanceof PsiMethodCallExpression)) {
208               return false;
209             }
210             PsiMethodCallExpression methodCall = (PsiMethodCallExpression) parent;
211             PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
212             if (arguments.length < paramValues.length) return false;
213
214             boolean needFurtherProcess = false;
215             for (int i = 0; i < paramValues.length; i++) {
216               Object value = paramValues[i];
217               final String currentArg = getArgValue(arguments[i]);
218               if (value == null) {
219                 paramValues[i] = currentArg;
220                 if (currentArg != NOT_CONST) {
221                   needFurtherProcess = true;
222                 }
223               } else if (value != NOT_CONST) {
224                 if (!paramValues[i].equals(currentArg)) {
225                   paramValues[i] = NOT_CONST;
226                 } else {
227                   needFurtherProcess = true;
228                 }
229               }
230             }
231
232             return needFurtherProcess;
233           })) {
234             for (int i = 0, length = paramValues.length; i < length; i++) {
235               String value = paramValues[i];
236               if (value != null && value != NOT_CONST) {
237                 holder.registerProblem(registerProblem(holder.getManager(), parameters[i], value));
238               }
239             }
240           }
241         }
242       };
243     }
244
245     private String getArgValue(PsiExpression arg) {
246       return arg instanceof PsiLiteralExpression ? arg.getText() : NOT_CONST;
247     }
248   }
249
250   private ProblemDescriptor registerProblem(@NotNull InspectionManager manager,
251                                             PsiParameter parameter,
252                                             String value) {
253     final String name = parameter.getName();
254     return manager.createProblemDescriptor(ObjectUtils.notNull(parameter.getNameIdentifier(), parameter),
255                                            InspectionsBundle.message("inspection.same.parameter.problem.descriptor",
256                                                                      "<code>" + name + "</code>",
257                                                                      "<code>" + value + "</code>"),
258                                            createFix(name, value),
259                                            ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false);
260   }
261 }