replaced <code></code> with more concise {@code}
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / codeInspection / unused / defaultParameter / GrUnusedDefaultParameterInspection.java
1 /*
2  * Copyright 2000-2017 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 org.jetbrains.plugins.groovy.codeInspection.unused.defaultParameter;
17
18 import com.intellij.codeInsight.intention.QuickFixFactory;
19 import com.intellij.codeInspection.CleanupLocalInspectionTool;
20 import com.intellij.codeInspection.LocalInspectionTool;
21 import com.intellij.codeInspection.ProblemHighlightType;
22 import com.intellij.codeInspection.ProblemsHolder;
23 import com.intellij.psi.PsiElement;
24 import com.intellij.psi.PsiElementVisitor;
25 import com.intellij.psi.impl.FindSuperElementsHelper;
26 import com.intellij.psi.search.searches.MethodReferencesSearch;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
29 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor;
30 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod;
35 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
36
37 import static org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle.message;
38
39 public class GrUnusedDefaultParameterInspection extends LocalInspectionTool implements CleanupLocalInspectionTool {
40
41   @NotNull
42   @Override
43   public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
44     return new GroovyPsiElementVisitor(new GroovyElementVisitor() {
45       @Override
46       public void visitExpression(@NotNull GrExpression expression) {
47         PsiElement expressionParent = expression.getParent();
48         if (!(expressionParent instanceof GrParameter)) return;
49
50         GrParameter parameter = (GrParameter)expressionParent;
51         if (parameter.getInitializerGroovy() != expression) return;
52
53         PsiElement parameterParent = parameter.getParent();
54         if (!(parameterParent instanceof GrParameterList)) return;
55
56         PsiElement parameterListParent = parameterParent.getParent();
57         if (!(parameterListParent instanceof GrMethod)) return;
58
59         GrMethod method = (GrMethod)parameterListParent;
60         if (PsiUtil.OPERATOR_METHOD_NAMES.contains(method.getName())) return;
61
62         if (isInitializerUnused(parameter, method)) {
63           holder.registerProblem(
64             expression, message("unused.default.parameter.message"), ProblemHighlightType.LIKE_UNUSED_SYMBOL,
65             QuickFixFactory.getInstance().createDeleteFix(expression, message("unused.default.parameter.fix"))
66           );
67         }
68       }
69     });
70   }
71
72   /**
73    * Consider following method:
74    * <pre>
75    *   def foo(a = 1, b = 2, c = 3) {}
76    * </pre>
77    * Its reflected methods:
78    * <pre>
79    *   def foo(a, b, c) {}
80    *   def foo(a, b) {}
81    *   def foo(a) {}
82    *   def foo() {}
83    * </pre>
84    * Initializer for '{@code a}' is used only when {@code foo} called without arguments,
85    * we do not care if {@code foo} is called with one, two ot three arguments.
86    * <p>
87    * In case of {@code b} we search {@code foo()} or {@code foo(1)} calls.
88    * <p>
89    * The general idea: search usages of last N reflected methods where N is number of current parameter among other default parameters.
90    */
91   private static boolean isInitializerUnused(@NotNull GrParameter parameter, @NotNull GrMethod method) {
92     int optionalParameterNumber = 0;
93     for (GrParameter someParameter : method.getParameters()) {
94       if (someParameter.isOptional()) optionalParameterNumber++;
95       if (someParameter == parameter) break;
96     }
97
98     GrReflectedMethod[] reflectedMethods = method.getReflectedMethods();
99     for (int i = reflectedMethods.length - optionalParameterNumber; i < reflectedMethods.length; i++) {
100       GrReflectedMethod reflectedMethod = reflectedMethods[i];
101       if (FindSuperElementsHelper.findSuperElements(reflectedMethod).length > 0) return false;
102       if (MethodReferencesSearch.search(reflectedMethod).findFirst() != null) return false;
103     }
104     return true;
105   }
106 }