common logic extracted
[idea/community.git] / plugins / InspectionGadgets / InspectionGadgetsAnalysis / src / com / siyeh / ig / naming / LambdaUnfriendlyMethodOverloadInspectionBase.java
1 /*
2  * Copyright 2000-2016 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.siyeh.ig.naming;
17
18 import com.intellij.psi.*;
19 import com.intellij.psi.util.PsiSuperMethodUtil;
20 import com.siyeh.InspectionGadgetsBundle;
21 import com.siyeh.ig.BaseInspection;
22 import com.siyeh.ig.BaseInspectionVisitor;
23 import org.jetbrains.annotations.Nls;
24 import org.jetbrains.annotations.NotNull;
25
26 import java.util.Objects;
27
28 /**
29  * @author Bas Leijdekkers
30  */
31 public class LambdaUnfriendlyMethodOverloadInspectionBase extends BaseInspection {
32   @Nls
33   @NotNull
34   @Override
35   public String getDisplayName() {
36     return InspectionGadgetsBundle.message("lambda.unfriendly.method.overload.display.name");
37   }
38
39   @NotNull
40   @Override
41   protected String buildErrorString(Object... infos) {
42     final PsiMethod method = (PsiMethod)infos[0];
43     return InspectionGadgetsBundle.message(method.isConstructor()
44                                            ? "lambda.unfriendly.constructor.overload.problem.descriptor"
45                                            : "lambda.unfriendly.method.overload.problem.descriptor");
46   }
47
48   @Override
49   public BaseInspectionVisitor buildVisitor() {
50     return new LambdaUnfriendlyMethodOverloadVisitor();
51   }
52
53   private static class LambdaUnfriendlyMethodOverloadVisitor extends BaseInspectionVisitor {
54
55     @Override
56     public void visitMethod(PsiMethod method) {
57       super.visitMethod(method);
58       final PsiParameterList parameterList = method.getParameterList();
59       final int parametersCount = parameterList.getParametersCount();
60       if (parametersCount == 0) {
61         return;
62       }
63       final PsiParameter[] parameters = parameterList.getParameters();
64       int functionalIndex = -1;
65       for (int i = 0; i < parameters.length; i++) {
66         final PsiParameter parameter = parameters[i];
67         if (LambdaUtil.isFunctionalType(parameter.getType())) {
68           functionalIndex = i;
69           break;
70         }
71       }
72        if (functionalIndex < 0) {
73          return;
74        }
75       final PsiClass containingClass = method.getContainingClass();
76       if (containingClass == null) {
77         return;
78       }
79       final String name = method.getName();
80       for (PsiMethod sameNameMethod : containingClass.findMethodsByName(name, true)) {
81         if (method.equals(sameNameMethod) || PsiSuperMethodUtil.isSuperMethod(method, sameNameMethod)) {
82           continue;
83         }
84         final PsiParameterList otherParameterList = sameNameMethod.getParameterList();
85         if (parametersCount != otherParameterList.getParametersCount()) {
86           continue;
87         }
88         final PsiParameter[] otherParameters = otherParameterList.getParameters();
89         final PsiType otherFunctionalType = otherParameters[functionalIndex].getType();
90         final PsiType functionalType = parameters[functionalIndex].getType();
91         if (!areOtherParameterTypesConvertible(parameters, otherParameters, functionalIndex) ||
92             !LambdaUtil.isFunctionalType(otherFunctionalType) ||
93             Objects.equals(functionalType, otherFunctionalType)) {
94           continue;
95         }
96
97         if (areSameShapeFunctionalTypes(functionalType, otherFunctionalType)) {
98           registerMethodError(method, method);
99           return;
100         }
101       }
102     }
103
104     private static boolean areSameShapeFunctionalTypes(PsiType one, PsiType two) {
105       final PsiMethod method1 = LambdaUtil.getFunctionalInterfaceMethod(one);
106       final PsiMethod method2 = LambdaUtil.getFunctionalInterfaceMethod(two);
107       if (method1 == null || method2 == null) {
108         return false;
109       }
110       final PsiType returnType1 = method1.getReturnType();
111       final PsiType returnType2 = method2.getReturnType();
112       if (PsiType.VOID.equals(returnType1) ^ PsiType.VOID.equals(returnType2)) {
113         return false;
114       }
115       return method1.getParameterList().getParametersCount() == method2.getParameterList().getParametersCount();
116     }
117
118     private static boolean areOtherParameterTypesConvertible(PsiParameter[] parameters, PsiParameter[] otherParameters, int notThisOne) {
119       for (int i = 0; i < parameters.length; i++) {
120         if (i == notThisOne) {
121           continue;
122         }
123         final PsiType type = parameters[i].getType();
124         final PsiType otherType = otherParameters[i].getType();
125         if (!type.isAssignableFrom(otherType) && !otherType.isAssignableFrom(type)) {
126           return false;
127         }
128       }
129       return true;
130     }
131   }
132 }