2 * Copyright 2000-2014 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.
16 package com.intellij.codeInspection.sameParameterValue;
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;
33 import java.util.ArrayList;
34 import java.util.List;
39 public class SameParameterValueInspectionBase extends GlobalJavaBatchInspectionTool {
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;
51 if (refMethod.hasSuperMethods()) return null;
53 if (refMethod.isEntry()) return null;
55 RefParameter[] parameters = refMethod.getParameters();
56 for (RefParameter refParameter : parameters) {
57 String value = refParameter.getActualValueIfSame();
59 if (!globalContext.shouldCheck(refParameter, this)) continue;
60 if (problems == null) problems = new ArrayList<>(1);
61 problems.add(registerProblem(manager, refParameter.getElement(), value));
66 return problems == null ? null : problems.toArray(new CommonProblemDescriptor[problems.size()]);
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() {
79 public boolean process(PsiReference psiReference) {
80 processor.ignoreElement(refMethod);
95 public String getDisplayName() {
96 return InspectionsBundle.message("inspection.same.parameter.display.name");
101 public String getGroupDisplayName() {
102 return GroupNames.DECLARATION_REDUNDANCY;
107 public String getShortName() {
108 return "SameParameterValue";
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);
122 protected LocalQuickFix createFix(String paramName, String value) {
128 public String getHint(@NotNull final QuickFix fix) {
129 return fix.toString();
134 public LocalInspectionTool getSharedLocalInspectionTool() {
135 return new LocalSameParameterValueInspection(this);
138 private class LocalSameParameterValueInspection extends BaseJavaLocalInspectionTool {
139 private static final String NOT_CONST = "_NOT_CONST";
141 private final SameParameterValueInspectionBase myGlobal;
143 private LocalSameParameterValueInspection(SameParameterValueInspectionBase global) {
149 public String getGroupDisplayName() {
150 return myGlobal.getGroupDisplayName();
155 public String getDisplayName() {
156 return myGlobal.getDisplayName();
161 public String getShortName() {
162 return myGlobal.getShortName();
167 public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
169 @NotNull LocalInspectionToolSession session) {
170 return new JavaElementVisitor() {
171 private final UnusedDeclarationInspectionBase myDeadCodeTool;
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;
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;
187 if (myDeadCodeTool.isEntryPoint(method)) return;
188 if (!method.getHierarchicalMethodSignature().getSuperSignatures().isEmpty()) return;
190 PsiParameter lastParameter = parameters[parameters.length - 1];
191 final String[] paramValues;
192 final boolean hasVarArg = lastParameter.getType() instanceof PsiEllipsisType;
194 if (parameters.length == 1) return;
195 paramValues = new String[parameters.length - 1];
197 paramValues = new String[parameters.length];
200 if (UnusedSymbolUtil.processUsages(holder.getProject(), method.getContainingFile(), method, new EmptyProgressIndicator(), null, info -> {
201 PsiElement element = info.getElement();
203 if (!(element instanceof PsiReferenceExpression)) {
206 PsiElement parent = element.getParent();
207 if (!(parent instanceof PsiMethodCallExpression)) {
210 PsiMethodCallExpression methodCall = (PsiMethodCallExpression) parent;
211 PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
212 if (arguments.length < paramValues.length) return false;
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]);
219 paramValues[i] = currentArg;
220 if (currentArg != NOT_CONST) {
221 needFurtherProcess = true;
223 } else if (value != NOT_CONST) {
224 if (!paramValues[i].equals(currentArg)) {
225 paramValues[i] = NOT_CONST;
227 needFurtherProcess = true;
232 return needFurtherProcess;
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));
245 private String getArgValue(PsiExpression arg) {
246 return arg instanceof PsiLiteralExpression ? arg.getText() : NOT_CONST;
250 private ProblemDescriptor registerProblem(@NotNull InspectionManager manager,
251 PsiParameter parameter,
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);