Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / unusedParameters / UnusedParametersInspection.java
blob54be3627bd9ad2aeb839a831a4fe993d179a9b3b
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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.
18 * Created by IntelliJ IDEA.
19 * User: max
20 * Date: Dec 24, 2001
21 * Time: 2:46:32 PM
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com.intellij.codeInspection.unusedParameters;
27 import com.intellij.analysis.AnalysisScope;
28 import com.intellij.codeInsight.daemon.GroupNames;
29 import com.intellij.codeInspection.*;
30 import com.intellij.codeInspection.reference.*;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.util.Comparing;
33 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
34 import com.intellij.psi.*;
35 import com.intellij.psi.search.PsiReferenceProcessor;
36 import com.intellij.psi.search.PsiReferenceProcessorAdapter;
37 import com.intellij.psi.search.PsiSearchHelper;
38 import com.intellij.psi.search.searches.OverridingMethodsSearch;
39 import com.intellij.psi.search.searches.ReferencesSearch;
40 import com.intellij.psi.util.PsiModificationTracker;
41 import com.intellij.psi.util.PsiTreeUtil;
42 import com.intellij.psi.util.PsiUtilBase;
43 import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
44 import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.List;
52 public class UnusedParametersInspection extends GlobalJavaInspectionTool {
54 @Nullable
55 public CommonProblemDescriptor[] checkElement(final RefEntity refEntity,
56 final AnalysisScope scope,
57 final InspectionManager manager,
58 final GlobalInspectionContext globalContext,
59 final ProblemDescriptionsProcessor processor) {
60 if (refEntity instanceof RefMethod) {
61 final RefMethod refMethod = (RefMethod)refEntity;
63 if (refMethod.isSyntheticJSP()) return null;
65 if (refMethod.isExternalOverride()) return null;
67 if (!(refMethod.isStatic() || refMethod.isConstructor()) && refMethod.getSuperMethods().size() > 0) return null;
69 if ((refMethod.isAbstract() || refMethod.getOwnerClass().isInterface()) && refMethod.getDerivedMethods().isEmpty()) return null;
71 if (RefUtil.isEntryPoint(refMethod)) return null;
73 final ArrayList<RefParameter> unusedParameters = getUnusedParameters(refMethod);
75 if (unusedParameters.size() == 0) return null;
77 final List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
78 for (RefParameter refParameter : unusedParameters) {
79 final PsiIdentifier psiIdentifier = refParameter.getElement().getNameIdentifier();
80 if (psiIdentifier != null) {
81 result.add(manager.createProblemDescriptor(psiIdentifier,
82 refMethod.isAbstract()
83 ? InspectionsBundle.message("inspection.unused.parameter.composer")
84 : InspectionsBundle.message("inspection.unused.parameter.composer1"),
85 new AcceptSuggested(globalContext.getRefManager(), processor, refParameter.toString()),
86 ProblemHighlightType.LIKE_UNUSED_SYMBOL, false));
89 return result.toArray(new CommonProblemDescriptor[result.size()]);
91 return null;
94 protected boolean queryExternalUsagesRequests(final RefManager manager, final GlobalJavaInspectionContext globalContext,
95 final ProblemDescriptionsProcessor processor) {
96 final Project project = manager.getProject();
97 for (RefElement entryPoint : globalContext.getEntryPointsManager(manager).getEntryPoints()) {
98 processor.ignoreElement(entryPoint);
101 final PsiSearchHelper helper = PsiManager.getInstance(project).getSearchHelper();
102 final AnalysisScope scope = manager.getScope();
103 manager.iterate(new RefJavaVisitor() {
104 @Override public void visitElement(RefEntity refEntity) {
105 if (refEntity instanceof RefMethod) {
106 RefMethod refMethod = (RefMethod)refEntity;
107 final PsiModifierListOwner element = refMethod.getElement();
108 if (element instanceof PsiMethod) { //implicit construcors are invisible
109 PsiMethod psiMethod = (PsiMethod)element;
110 if (!refMethod.isStatic() && !refMethod.isConstructor() && !PsiModifier.PRIVATE.equals(refMethod.getAccessModifier())) {
111 final ArrayList<RefParameter> unusedParameters = getUnusedParameters(refMethod);
112 if (unusedParameters.isEmpty()) return;
113 PsiMethod[] derived = OverridingMethodsSearch.search(psiMethod, psiMethod.getUseScope(), true).toArray(PsiMethod.EMPTY_ARRAY);
114 for (final RefParameter refParameter : unusedParameters) {
115 if (refMethod.isAbstract() && derived.length == 0) {
116 refParameter.parameterReferenced(false);
117 processor.ignoreElement(refParameter);
119 else {
120 int idx = refParameter.getIndex();
121 final boolean[] found = new boolean[]{false};
122 for (int i = 0; i < derived.length && !found[0]; i++) {
123 if (!scope.contains(derived[i])) {
124 PsiParameter psiParameter = derived[i].getParameterList().getParameters()[idx];
125 ReferencesSearch.search(psiParameter, helper.getUseScope(psiParameter), false).forEach(new PsiReferenceProcessorAdapter(
126 new PsiReferenceProcessor() {
127 public boolean execute(PsiReference element) {
128 refParameter.parameterReferenced(false);
129 processor.ignoreElement(refParameter);
130 found[0] = true;
131 return false;
133 }));
143 return false;
146 @Nullable
147 public String getHint(final QuickFix fix) {
148 return ((AcceptSuggested)fix).getHint();
151 @Nullable
152 public QuickFix getQuickFix(final String hint) {
153 return new AcceptSuggested(null, null, hint);
156 public void compose(final StringBuffer buf, final RefEntity refEntity, final HTMLComposer composer) {
157 if (refEntity instanceof RefMethod) {
158 final RefMethod refMethod = (RefMethod)refEntity;
159 final HTMLJavaHTMLComposer javaComposer = composer.getExtension(HTMLJavaHTMLComposer.COMPOSER);
160 javaComposer.appendDerivedMethods(buf, refMethod);
161 javaComposer.appendSuperMethods(buf, refMethod);
165 public static ArrayList<RefParameter> getUnusedParameters(RefMethod refMethod) {
166 boolean checkDeep = !refMethod.isStatic() && !refMethod.isConstructor();
167 ArrayList<RefParameter> res = new ArrayList<RefParameter>();
168 RefParameter[] methodParameters = refMethod.getParameters();
169 RefParameter[] result = new RefParameter[methodParameters.length];
170 System.arraycopy(methodParameters, 0, result, 0, methodParameters.length);
172 clearUsedParameters(refMethod, result, checkDeep);
174 for (RefParameter parameter : result) {
175 if (parameter != null) {
176 res.add(parameter);
180 return res;
183 private static void clearUsedParameters(@NotNull RefMethod refMethod, RefParameter[] params, boolean checkDeep) {
184 RefParameter[] methodParms = refMethod.getParameters();
186 for (int i = 0; i < methodParms.length; i++) {
187 if (methodParms[i].isUsedForReading()) params[i] = null;
190 if (checkDeep) {
191 for (RefMethod refOverride : refMethod.getDerivedMethods()) {
192 clearUsedParameters(refOverride, params, checkDeep);
197 @NotNull
198 public String getDisplayName() {
199 return InspectionsBundle.message("inspection.unused.parameter.display.name");
202 @NotNull
203 public String getGroupDisplayName() {
204 return GroupNames.DECLARATION_REDUNDANCY;
207 @NotNull
208 public String getShortName() {
209 return "UnusedParameters";
213 private static class AcceptSuggested implements LocalQuickFix {
214 private final RefManager myManager;
215 private final String myHint;
216 private final ProblemDescriptionsProcessor myProcessor;
218 public AcceptSuggested(final RefManager manager, final ProblemDescriptionsProcessor processor, final String hint) {
219 myManager = manager;
220 myProcessor = processor;
221 myHint = hint;
224 public String getHint() {
225 return myHint;
228 @NotNull
229 public String getName() {
230 return InspectionsBundle.message("inspection.unused.parameter.delete.quickfix");
233 @NotNull
234 public String getFamilyName() {
235 return getName();
238 public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
239 if (ReadonlyStatusHandler.getInstance(project)
240 .ensureFilesWritable(PsiUtilBase.getVirtualFile(descriptor.getPsiElement())).hasReadonlyFiles()) return;
241 final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethod.class);
242 if (psiMethod != null) {
243 final RefElement refMethod = myManager.getReference(psiMethod);
244 if (refMethod != null) {
245 final ArrayList<PsiElement> psiParameters = new ArrayList<PsiElement>();
246 if (myManager != null) {
247 for (final RefParameter refParameter : getUnusedParameters((RefMethod)refMethod)) {
248 psiParameters.add(refParameter.getElement());
251 else {
252 final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
253 for (PsiParameter parameter : parameters) {
254 if (Comparing.strEqual(parameter.getName(), myHint)) {
255 psiParameters.add(parameter);
256 break;
261 final PsiModificationTracker tracker = psiMethod.getManager().getModificationTracker();
262 final long startModificationCount = tracker.getModificationCount();
264 removeUnusedParameterViaChangeSignature(psiMethod, psiParameters);
266 if (myManager != null && startModificationCount != tracker.getModificationCount()) {
267 myProcessor.ignoreElement(refMethod);
273 private static void removeUnusedParameterViaChangeSignature(final PsiMethod psiMethod,
274 final Collection<PsiElement> parametersToDelete) {
275 ArrayList<ParameterInfoImpl> newParameters = new ArrayList<ParameterInfoImpl>();
276 PsiParameter[] oldParameters = psiMethod.getParameterList().getParameters();
277 for (int i = 0; i < oldParameters.length; i++) {
278 PsiParameter oldParameter = oldParameters[i];
279 if (!parametersToDelete.contains(oldParameter)) {
280 newParameters.add(new ParameterInfoImpl(i, oldParameter.getName(), oldParameter.getType()));
284 ParameterInfoImpl[] parameterInfos = newParameters.toArray(new ParameterInfoImpl[newParameters.size()]);
286 ChangeSignatureProcessor csp = new ChangeSignatureProcessor(psiMethod.getProject(), psiMethod, false, null, psiMethod.getName(),
287 psiMethod.getReturnType(), parameterInfos);
289 csp.run();