f5b690863957d5f809596577cc8f5023a77f8110
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / scope / conflictResolvers / JavaMethodsConflictResolver.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.psi.scope.conflictResolvers;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.progress.ProgressManager;
20 import com.intellij.openapi.projectRoots.JavaSdkVersion;
21 import com.intellij.openapi.projectRoots.JavaVersionService;
22 import com.intellij.openapi.util.Comparing;
23 import com.intellij.pom.java.LanguageLevel;
24 import com.intellij.psi.*;
25 import com.intellij.psi.impl.PsiSuperMethodImplUtil;
26 import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
27 import com.intellij.psi.infos.CandidateInfo;
28 import com.intellij.psi.infos.MethodCandidateInfo;
29 import com.intellij.psi.scope.PsiConflictResolver;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.util.*;
32 import com.intellij.util.containers.HashSet;
33 import gnu.trove.THashMap;
34 import gnu.trove.THashSet;
35 import gnu.trove.TIntArrayList;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43
44 /**
45  * Created by IntelliJ IDEA.
46  * User: ik
47  * Date: 10.06.2003
48  * Time: 19:41:51
49  * To change this template use Options | File Templates.
50  */
51 public class JavaMethodsConflictResolver implements PsiConflictResolver{
52   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver");
53
54   private final PsiElement myArgumentsList;
55   private PsiType[] myActualParameterTypes;
56   protected LanguageLevel myLanguageLevel;
57
58   public JavaMethodsConflictResolver(@NotNull PsiExpressionList list, @NotNull LanguageLevel languageLevel) {
59     this(list, null, languageLevel);
60   }
61
62   public JavaMethodsConflictResolver(@NotNull PsiElement argumentsList,
63                                      PsiType[] actualParameterTypes,
64                                      @NotNull LanguageLevel languageLevel) {
65     myArgumentsList = argumentsList;
66     myActualParameterTypes = actualParameterTypes;
67     myLanguageLevel = languageLevel;
68   }
69
70   @Override
71   public CandidateInfo resolveConflict(@NotNull List<CandidateInfo> conflicts){
72     if (conflicts.isEmpty()) return null;
73     if (conflicts.size() == 1) return conflicts.get(0);
74
75     boolean atLeastOneMatch = checkParametersNumber(conflicts, getActualParametersLength(), true);
76     if (conflicts.size() == 1) return conflicts.get(0);
77
78     checkSameSignatures(conflicts);
79     if (conflicts.size() == 1) return conflicts.get(0);
80
81     checkAccessStaticLevels(conflicts, true);
82     if (conflicts.size() == 1) return conflicts.get(0);
83
84     checkParametersNumber(conflicts, getActualParametersLength(), false);
85     if (conflicts.size() == 1) return conflicts.get(0);
86
87     final int applicabilityLevel = checkApplicability(conflicts);
88     if (conflicts.size() == 1) return conflicts.get(0);
89
90     // makes no sense to do further checks, because if no one candidate matches by parameters count
91     // then noone can be more specific
92     if (!atLeastOneMatch) return null;
93
94     checkLambdaApplicable(conflicts, myLanguageLevel);
95     if (conflicts.size() == 1) return conflicts.get(0);
96
97     checkSpecifics(conflicts, applicabilityLevel, myLanguageLevel);
98     if (conflicts.size() == 1) return conflicts.get(0);
99
100     checkPrimitiveVarargs(conflicts, getActualParametersLength());
101     if (conflicts.size() == 1) return conflicts.get(0);
102
103     checkAccessStaticLevels(conflicts, false);
104     if (conflicts.size() == 1) return conflicts.get(0);
105
106     Set<CandidateInfo> uniques = new THashSet<CandidateInfo>(conflicts);
107     if (uniques.size() == 1) return uniques.iterator().next();
108     return null;
109   }
110
111   private void checkLambdaApplicable(@NotNull List<CandidateInfo> conflicts, @NotNull LanguageLevel languageLevel) {
112     if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) return;
113     for (int i = 0; i < getActualParametersLength(); i++) {
114
115       PsiExpression expression;
116       if (myArgumentsList instanceof PsiExpressionList) {
117         expression = ((PsiExpressionList)myArgumentsList).getExpressions()[i];
118       }
119       else {
120         final PsiType argType = getActualParameterTypes()[i];
121         expression = argType instanceof PsiLambdaExpressionType ? ((PsiLambdaExpressionType)argType).getExpression() : null;
122       }
123
124       final PsiLambdaExpression lambdaExpression = findNestedLambdaExpression(expression);
125       if (lambdaExpression != null) {
126         checkLambdaApplicable(conflicts, i, lambdaExpression);
127       }
128     }
129   }
130
131   private static PsiLambdaExpression findNestedLambdaExpression(PsiExpression expression) {
132     if (expression instanceof PsiLambdaExpression) {
133       return (PsiLambdaExpression)expression;
134     }
135     else if (expression instanceof PsiParenthesizedExpression) {
136       return findNestedLambdaExpression(((PsiParenthesizedExpression)expression).getExpression());
137     }
138     else if (expression instanceof PsiConditionalExpression) {
139       PsiLambdaExpression lambdaExpression = findNestedLambdaExpression(((PsiConditionalExpression)expression).getThenExpression());
140       if (lambdaExpression != null) {
141         return lambdaExpression;
142       }
143       return findNestedLambdaExpression(((PsiConditionalExpression)expression).getElseExpression());
144     }
145     return null;
146   }
147
148   private static void checkLambdaApplicable(@NotNull List<CandidateInfo> conflicts, int i, @NotNull PsiLambdaExpression lambdaExpression) {
149     for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
150       ProgressManager.checkCanceled();
151       final CandidateInfo conflict = iterator.next();
152       final PsiMethod method = (PsiMethod)conflict.getElement();
153       final PsiParameter[] methodParameters = method.getParameterList().getParameters();
154       if (methodParameters.length == 0) continue;
155       final PsiParameter param = i < methodParameters.length ? methodParameters[i] : methodParameters[methodParameters.length - 1];
156       final PsiType paramType = param.getType();
157       // http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.1
158       // A lambda expression or a method reference expression is potentially compatible with a type variable if the type variable is a type parameter of the candidate method.
159       final PsiClass paramClass = PsiUtil.resolveClassInType(paramType);
160       if (paramClass instanceof PsiTypeParameter && ((PsiTypeParameter)paramClass).getOwner() == method) continue;
161       if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType),
162                                          InferenceSession.isPertinentToApplicability(lambdaExpression, method))) {
163         iterator.remove();
164       }
165     }
166   }
167
168   public void checkSpecifics(@NotNull List<CandidateInfo> conflicts,
169                              @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel,
170                              @NotNull LanguageLevel languageLevel) {
171     final boolean applicable = applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;
172
173     int conflictsCount = conflicts.size();
174     // Specifics
175     if (applicable) {
176       final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
177       for (int i = 1; i < conflictsCount; i++) {
178         final CandidateInfo method = newConflictsArray[i];
179         for (int j = 0; j < i; j++) {
180           ProgressManager.checkCanceled();
181           final CandidateInfo conflict = newConflictsArray[j];
182           if (nonComparable(method, conflict, applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY)) continue; 
183           switch (isMoreSpecific((MethodCandidateInfo)method, (MethodCandidateInfo)conflict, applicabilityLevel, languageLevel)) {
184             case FIRST:
185               conflicts.remove(conflict);
186               break;
187             case SECOND:
188               conflicts.remove(method);
189               break;
190             case NEITHER:
191               break;
192           }
193         }
194       }
195     }
196   }
197
198   protected boolean nonComparable(@NotNull CandidateInfo method, @NotNull CandidateInfo conflict, boolean fixedArity) {
199     assert method != conflict;
200     return false;
201   }
202
203   protected static void checkAccessStaticLevels(@NotNull List<CandidateInfo> conflicts, boolean checkAccessible) {
204     int conflictsCount = conflicts.size();
205
206     int maxCheckLevel = -1;
207     int[] checkLevels = new int[conflictsCount];
208     int index = 0;
209     for (final CandidateInfo conflict : conflicts) {
210       ProgressManager.checkCanceled();
211       final MethodCandidateInfo method = (MethodCandidateInfo)conflict;
212       final int level = checkAccessible ? getCheckAccessLevel(method) : getCheckStaticLevel(method);
213       checkLevels[index++] = level;
214       maxCheckLevel = Math.max(maxCheckLevel, level);
215     }
216
217     for (int i = conflictsCount - 1; i >= 0; i--) {
218       // check for level
219       if (checkLevels[i] < maxCheckLevel) {
220         conflicts.remove(i);
221       }
222     }
223   }
224
225   protected void checkSameSignatures(@NotNull List<CandidateInfo> conflicts) {
226     // candidates should go in order of class hierarchy traversal
227     // in order for this to work
228     Map<MethodSignature, CandidateInfo> signatures = new THashMap<MethodSignature, CandidateInfo>(conflicts.size());
229     Set<PsiMethod> superMethods = new HashSet<PsiMethod>();
230     for (CandidateInfo conflict : conflicts) {
231       final PsiMethod method = ((MethodCandidateInfo)conflict).getElement();
232       for (HierarchicalMethodSignature methodSignature : method.getHierarchicalMethodSignature().getSuperSignatures()) {
233         final PsiMethod superMethod = methodSignature.getMethod();
234         final PsiClass aClass = superMethod.getContainingClass();
235         if (aClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
236           superMethods.add(superMethod);
237         }
238       }
239     }
240     nextConflict:
241     for (int i=0; i<conflicts.size();i++) {
242       ProgressManager.checkCanceled();
243       CandidateInfo info = conflicts.get(i);
244       PsiMethod method = (PsiMethod)info.getElement();
245
246       if (!method.hasModifierProperty(PsiModifier.STATIC) && superMethods.contains(method)) {
247         conflicts.remove(i);
248         i--;
249         continue;
250       }
251
252       PsiClass class1 = method.getContainingClass();
253       PsiSubstitutor infoSubstitutor = ((MethodCandidateInfo)info).getSubstitutor(false);
254       MethodSignature signature = method.getSignature(infoSubstitutor);
255       CandidateInfo existing = signatures.get(signature);
256
257       if (existing == null) {
258         signatures.put(signature, info);
259         continue;
260       }
261       PsiMethod existingMethod = (PsiMethod)existing.getElement();
262       PsiClass existingClass = existingMethod.getContainingClass();
263       if (class1 != null && existingClass != null) { //prefer interface methods to methods from Object
264         if (class1.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) {
265           signatures.put(signature, info);
266           continue;
267         } 
268         else if (existingClass.isInterface() && CommonClassNames.JAVA_LANG_OBJECT.equals(class1.getQualifiedName())) {
269           conflicts.remove(info);
270           i--;
271           continue;
272         }
273       }
274       if (method == existingMethod) {
275         PsiElement scope1 = info.getCurrentFileResolveScope();
276         PsiElement scope2 = existing.getCurrentFileResolveScope();
277         if (scope1 instanceof PsiClass &&
278             scope2 instanceof PsiClass &&
279             PsiTreeUtil.isAncestor(scope1, scope2, true) &&
280             !existing.isAccessible()) { //prefer methods from outer class to inaccessible base class methods
281           signatures.put(signature, info);
282           continue;
283         }
284       }
285
286       // filter out methods with incorrect inferred bounds (for unrelated methods only)
287       boolean existingTypeParamAgree = areTypeParametersAgree(existing);
288       boolean infoTypeParamAgree = areTypeParametersAgree(info);
289       if (existingTypeParamAgree && !infoTypeParamAgree && !PsiSuperMethodImplUtil.isSuperMethodSmart(method, existingMethod)) {
290         conflicts.remove(i);
291         i--;
292         continue;
293       }
294       if (!existingTypeParamAgree && infoTypeParamAgree && !PsiSuperMethodImplUtil.isSuperMethodSmart(existingMethod, method)) {
295         signatures.put(signature, info);
296         int index = conflicts.indexOf(existing);
297         conflicts.remove(index);
298         i--;
299         continue;
300       }
301
302       if (InheritanceUtil.isInheritorOrSelf(class1, existingClass, true) ||
303           InheritanceUtil.isInheritorOrSelf(existingClass, class1, true)) {
304         PsiParameter[] parameters = method.getParameterList().getParameters();
305         final PsiParameter[] existingParameters = existingMethod.getParameterList().getParameters();
306         for (int i1 = 0, parametersLength = parameters.length; i1 < parametersLength; i1++) {
307           if (parameters[i1].getType() instanceof PsiArrayType &&
308               !(existingParameters[i1].getType() instanceof PsiArrayType)) {//prefer more specific type
309             signatures.put(signature, info);
310             continue nextConflict;
311           }
312         }
313         PsiType returnType1 = method.getReturnType();
314         PsiType returnType2 = existingMethod.getReturnType();
315         if (returnType1 != null && returnType2 != null) {
316           returnType1 = infoSubstitutor.substitute(returnType1);
317           returnType2 = ((MethodCandidateInfo)existing).getSubstitutor(false).substitute(returnType2);
318           if (!returnType1.equals(returnType2) && returnType1.isAssignableFrom(returnType2)) {
319             conflicts.remove(i);
320             i--;
321             continue;
322           }
323         }
324
325         // prefer derived class
326         signatures.put(signature, info);
327       }
328       else {
329         final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(myArgumentsList, PsiMethodCallExpression.class);
330         if (methodCallExpression != null) {
331           final PsiReferenceExpression expression = methodCallExpression.getMethodExpression();
332           final PsiExpression qualifierExpression = expression.getQualifierExpression();
333           PsiClass currentClass;
334           if (qualifierExpression != null) {
335             currentClass = PsiUtil.resolveClassInClassTypeOnly(qualifierExpression.getType());
336           }
337           else {
338             currentClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class);
339           }
340
341           if (currentClass != null && existingClass != null && class1 != null) {
342             final PsiSubstitutor eSubstitutor = TypeConversionUtil.getMaybeSuperClassSubstitutor(existingClass, currentClass, PsiSubstitutor.EMPTY, null);
343             final PsiSubstitutor cSubstitutor = TypeConversionUtil.getMaybeSuperClassSubstitutor(class1, currentClass, PsiSubstitutor.EMPTY, null);
344             if (eSubstitutor != null && cSubstitutor != null &&
345                 MethodSignatureUtil.areSignaturesEqual(existingMethod.getSignature(eSubstitutor), method.getSignature(cSubstitutor))) {
346               final PsiType returnType = eSubstitutor.substitute(existingMethod.getReturnType());
347               final PsiType returnType1 = cSubstitutor.substitute(method.getReturnType());
348               if (returnType != null && returnType1 != null && !returnType1.equals(returnType)) {
349                 if (TypeConversionUtil.isAssignable(returnType, returnType1, false)) {
350                   if (class1.isInterface() && !existingClass.isInterface()) continue;
351                   conflicts.remove(existing);
352                 } else {
353                   if (!TypeConversionUtil.isAssignable(returnType1, returnType, false)) continue;
354                   conflicts.remove(i);
355                 }
356                 i--;
357                 break;
358               }
359             }
360           }
361         }
362       }
363     }
364   }
365
366   private static boolean areTypeParametersAgree(@NotNull CandidateInfo info) {
367     return ((MethodCandidateInfo)info).getPertinentApplicabilityLevel() != MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;
368   }
369
370   private static boolean checkParametersNumber(@NotNull List<CandidateInfo> conflicts,
371                                                final int argumentsCount,
372                                                boolean ignoreIfStaticsProblem) {
373     boolean atLeastOneMatch = false;
374     TIntArrayList unmatchedIndices = null;
375     for (int i = 0; i < conflicts.size(); i++) {
376       ProgressManager.checkCanceled();
377       CandidateInfo info = conflicts.get(i);
378       if (ignoreIfStaticsProblem && !info.isStaticsScopeCorrect()) return true;
379       if (!(info instanceof MethodCandidateInfo)) continue;
380       PsiMethod method = ((MethodCandidateInfo)info).getElement();
381       if (method.isVarArgs()) return true;
382       if (method.getParameterList().getParametersCount() == argumentsCount) {
383         // remove all unmatched before
384         if (unmatchedIndices != null) {
385           for (int u=unmatchedIndices.size()-1; u>=0; u--) {
386             int index = unmatchedIndices.get(u);
387             conflicts.remove(index);
388             i--;
389           }
390           unmatchedIndices = null;
391         }
392         atLeastOneMatch = true;
393       }
394       else if (atLeastOneMatch) {
395         conflicts.remove(i);
396         i--;
397       }
398       else {
399         if (unmatchedIndices == null) unmatchedIndices = new TIntArrayList(conflicts.size()-i);
400         unmatchedIndices.add(i);
401       }
402     }
403
404     return atLeastOneMatch;
405   }
406
407   @MethodCandidateInfo.ApplicabilityLevelConstant
408   public int checkApplicability(@NotNull List<CandidateInfo> conflicts) {
409     @MethodCandidateInfo.ApplicabilityLevelConstant int maxApplicabilityLevel = 0;
410     boolean toFilter = false;
411     for (CandidateInfo conflict : conflicts) {
412       ProgressManager.checkCanceled();
413       @MethodCandidateInfo.ApplicabilityLevelConstant final int level = getPertinentApplicabilityLevel((MethodCandidateInfo)conflict);
414       if (maxApplicabilityLevel > 0 && maxApplicabilityLevel != level) {
415         toFilter = true;
416       }
417       if (level > maxApplicabilityLevel) {
418         maxApplicabilityLevel = level;
419       }
420     }
421
422     if (toFilter) {
423       for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext();) {
424         ProgressManager.checkCanceled();
425         CandidateInfo info = iterator.next();
426         final int level = getPertinentApplicabilityLevel((MethodCandidateInfo)info);
427         if (level < maxApplicabilityLevel) {
428           iterator.remove();
429         }
430       }
431     }
432
433     return maxApplicabilityLevel;
434   }
435
436   protected int getPertinentApplicabilityLevel(@NotNull MethodCandidateInfo conflict) {
437     return conflict.getPertinentApplicabilityLevel();
438   }
439
440   private static int getCheckAccessLevel(@NotNull MethodCandidateInfo method){
441     boolean visible = method.isAccessible();
442     return visible ? 1 : 0;
443   }
444
445   private static int getCheckStaticLevel(@NotNull MethodCandidateInfo method){
446     boolean available = method.isStaticsScopeCorrect();
447     return (available ? 1 : 0) << 1 |
448            (method.getCurrentFileResolveScope() instanceof PsiImportStaticStatement ? 0 : 1);
449   }
450
451   @NotNull
452   private PsiType[] getActualParameterTypes() {
453     PsiType[] types = myActualParameterTypes;
454     if (types == null) {
455       LOG.assertTrue(myArgumentsList instanceof PsiExpressionList, myArgumentsList);
456       myActualParameterTypes = types = getArgumentTypes();
457     }
458     return types;
459   }
460
461   private int getActualParametersLength() {
462     if (myActualParameterTypes == null) {
463       LOG.assertTrue(myArgumentsList instanceof PsiExpressionList, myArgumentsList);
464       return ((PsiExpressionList)myArgumentsList).getExpressions().length;
465     }
466     return myActualParameterTypes.length;
467   }
468
469   @NotNull
470   protected PsiType[] getArgumentTypes() {
471     return ((PsiExpressionList)myArgumentsList).getExpressionTypes();
472   }
473
474   private enum Specifics {
475     FIRST,
476     SECOND,
477     NEITHER
478   }
479
480   private static boolean isBoxingHappened(PsiType argType, PsiType parameterType, @NotNull LanguageLevel languageLevel) {
481     if (argType == null) return parameterType instanceof PsiPrimitiveType;
482     if (parameterType instanceof PsiClassType) {
483       parameterType = ((PsiClassType)parameterType).setLanguageLevel(languageLevel);
484     }
485
486     return TypeConversionUtil.boxingConversionApplicable(parameterType, argType);
487   }
488
489   private Specifics isMoreSpecific(@NotNull MethodCandidateInfo info1,
490                                    @NotNull MethodCandidateInfo info2,
491                                    @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel,
492                                    @NotNull LanguageLevel languageLevel) {
493     PsiMethod method1 = info1.getElement();
494     PsiMethod method2 = info2.getElement();
495     final PsiClass class1 = method1.getContainingClass();
496     final PsiClass class2 = method2.getContainingClass();
497
498     final PsiParameter[] params1 = method1.getParameterList().getParameters();
499     final PsiParameter[] params2 = method2.getParameterList().getParameters();
500
501     final PsiTypeParameter[] typeParameters1 = method1.getTypeParameters();
502     final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters();
503     final PsiSubstitutor classSubstitutor1 = info1.getSubstitutor(false); //substitutions for method type parameters will be ignored
504     final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor(false);
505
506     final int max = Math.max(params1.length, params2.length);
507     PsiType[] types1 = PsiType.createArray(max);
508     PsiType[] types2 = PsiType.createArray(max);
509     final boolean varargsPosition = applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS;
510     for (int i = 0; i < max; i++) {
511       ProgressManager.checkCanceled();
512       PsiType type1 = params1.length > 0 ? params1[Math.min(i, params1.length - 1)].getType() : null;
513       PsiType type2 = params2.length > 0 ? params2[Math.min(i, params2.length - 1)].getType() : null;
514       if (varargsPosition) {
515         if (type1 instanceof PsiEllipsisType && type2 instanceof PsiEllipsisType &&
516             params1.length == params2.length &&
517             class1 != null && (!JavaVersionService.getInstance().isAtLeast(class1, JavaSdkVersion.JDK_1_7) || ((PsiArrayType)type1).getComponentType().equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || ((PsiArrayType)type2).getComponentType().equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) {
518           type1 = ((PsiEllipsisType)type1).toArrayType();
519           type2 = ((PsiEllipsisType)type2).toArrayType();
520         }
521         else {
522           type1 = type1 instanceof PsiEllipsisType ? ((PsiArrayType)type1).getComponentType() : type1;
523           type2 = type2 instanceof PsiEllipsisType ? ((PsiArrayType)type2).getComponentType() : type2;
524         }
525       }
526
527       types1[i] = type1;
528       types2[i] = type2;
529     }
530
531     boolean sameBoxing = true;
532     int[] boxingHappened = new int[2];
533     for (int i = 0; i < types1.length; i++) {
534       ProgressManager.checkCanceled();
535       PsiType type1 = classSubstitutor1.substitute(types1[i]);
536       PsiType type2 = classSubstitutor2.substitute(types2[i]);
537       PsiType argType = i < getActualParameterTypes().length ? getActualParameterTypes()[i] : null;
538
539       boolean boxingInFirst = false;
540       if (isBoxingHappened(argType, type1, languageLevel)) {
541         boxingHappened[0] += 1;
542         boxingInFirst = true;
543       }
544
545       boolean boxingInSecond = false;
546       if (isBoxingHappened(argType, type2, languageLevel)) {
547         boxingHappened[1] += 1;
548         boxingInSecond = true;
549       }
550       sameBoxing &= boxingInFirst == boxingInSecond;
551     }
552     if (boxingHappened[0] == 0 && boxingHappened[1] > 0) return Specifics.FIRST;
553     if (boxingHappened[0] > 0 && boxingHappened[1] == 0) return Specifics.SECOND;
554
555     if (sameBoxing) {
556       final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor();
557       final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor();
558
559       final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2);
560       final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1);
561
562       final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, method1, siteSubstitutor1, types1, types2AtSite,
563                                                                            languageLevel);
564       boolean applicable12 = isApplicableTo(types2AtSite, method1, languageLevel, varargsPosition, methodSubstitutor1, method2);
565
566       final PsiSubstitutor methodSubstitutor2 = calculateMethodSubstitutor(typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel);
567       boolean applicable21 = isApplicableTo(types1AtSite, method2, languageLevel, varargsPosition, methodSubstitutor2, method1);
568
569       if (!myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
570         final boolean typeArgsApplicable12 = GenericsUtil.isTypeArgumentsApplicable(typeParameters1, methodSubstitutor1, myArgumentsList, !applicable21);
571         final boolean typeArgsApplicable21 = GenericsUtil.isTypeArgumentsApplicable(typeParameters2, methodSubstitutor2, myArgumentsList, !applicable12);
572
573         if (!typeArgsApplicable12) {
574           applicable12 = false;
575         }
576
577         if (!typeArgsApplicable21) {
578           applicable21 = false;
579         }
580       }
581
582       if (applicable12 || applicable21) {
583
584         if (applicable12 && !applicable21) return Specifics.SECOND;
585         if (applicable21 && !applicable12) return Specifics.FIRST;
586
587         final boolean abstract1 = method1.hasModifierProperty(PsiModifier.ABSTRACT);
588         final boolean abstract2 = method2.hasModifierProperty(PsiModifier.ABSTRACT);
589         if (abstract1 && !abstract2) {
590           return Specifics.SECOND;
591         }
592         if (abstract2 && !abstract1) {
593           return Specifics.FIRST;
594         }
595
596       }
597
598       if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && myArgumentsList instanceof PsiExpressionList && (typeParameters1.length == 0 || typeParameters2.length == 0)) {
599         boolean toCompareFunctional = false;
600         if (types1.length > 0 && types2.length > 0) {
601           for (int i = 0; i < getActualParametersLength(); i++) {
602             final PsiType type1 = types1[Math.min(i, types1.length - 1)];
603             final PsiType type2 = types2[Math.min(i, types2.length - 1)];
604             //from 15.12.2.5 Choosing the Most Specific Method
605             //In addition, a functional interface type S is more specific than a functional interface type T for an expression exp 
606             // if T is not a subtype of S and one of the following conditions apply.
607             if (LambdaUtil.isFunctionalType(type1) && !TypeConversionUtil.erasure(type1).isAssignableFrom(type2) &&
608                 LambdaUtil.isFunctionalType(type2) && !TypeConversionUtil.erasure(type2).isAssignableFrom(type1)) {
609               types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL;
610               types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL;
611               toCompareFunctional = true;
612             }
613           }
614         }
615
616         if (toCompareFunctional) {
617           final boolean applicable12ignoreFunctionalType = isApplicableTo(types2AtSite, method1, languageLevel, varargsPosition,
618                                                                           calculateMethodSubstitutor(typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel), null);
619           final boolean applicable21ignoreFunctionalType = isApplicableTo(types1AtSite, method2, languageLevel, varargsPosition,
620                                                                           calculateMethodSubstitutor(typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel), null);
621
622           if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) {
623             Specifics specifics = null;
624             for (int i = 0; i < getActualParametersLength(); i++) {
625               if (types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL && 
626                   types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) {
627                 Specifics specific = isFunctionalTypeMoreSpecific(info1, info2, ((PsiExpressionList)myArgumentsList).getExpressions()[i], i);
628                 if (specific == Specifics.NEITHER) {
629                   specifics = Specifics.NEITHER;
630                   break;
631                 }
632   
633                 if (specifics == null) {
634                   specifics = specific;
635                 } else if (specifics != specific) {
636                   specifics = Specifics.NEITHER;
637                   break;
638                 }
639               }
640             }
641   
642             if (!applicable12ignoreFunctionalType && applicable21ignoreFunctionalType) {
643               return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER;
644             }
645   
646             if (!applicable21ignoreFunctionalType && applicable12ignoreFunctionalType) {
647               return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER;
648             }
649   
650             return specifics;
651           }
652         }
653       }
654     } 
655     else if (varargsPosition) {
656       final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]);
657       final PsiType lastParamType2 = classSubstitutor2.substitute(types2[types1.length - 1]);
658       final boolean assignable1 = TypeConversionUtil.isAssignable(lastParamType2, lastParamType1);
659       final boolean assignable2 = TypeConversionUtil.isAssignable(lastParamType1, lastParamType2);
660       if (assignable1 && !assignable2) {
661         return Specifics.FIRST;
662       }
663       if (assignable2 && !assignable1) {
664         return Specifics.SECOND;
665       }
666     }
667
668     if (class1 != class2) {
669       if (class2.isInheritor(class1, true) || class1.isInterface() && !class2.isInterface()) {
670         if (MethodSignatureUtil.isSubsignature(method1.getSignature(info1.getSubstitutor(false)), method2.getSignature(info2.getSubstitutor(false)))) {
671           return Specifics.SECOND;
672         }
673         else if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) {
674           return Specifics.SECOND;
675         }
676       }
677       else if (class1.isInheritor(class2, true) || class2.isInterface()) {
678         if (MethodSignatureUtil.areErasedParametersEqual(method1.getSignature(PsiSubstitutor.EMPTY), method2.getSignature(PsiSubstitutor.EMPTY)) && 
679             MethodSignatureUtil.isSubsignature(method2.getSignature(info2.getSubstitutor(false)), method1.getSignature(info1.getSubstitutor(false)))) {
680           return Specifics.FIRST;
681         }
682         else if (method1.hasModifierProperty(PsiModifier.STATIC) && method2.hasModifierProperty(PsiModifier.STATIC) && boxingHappened[0] == 0) {
683           return Specifics.FIRST;
684         }
685       }
686     }
687
688     final boolean raw1 = PsiUtil.isRawSubstitutor(method1, classSubstitutor1);
689     final boolean raw2 = PsiUtil.isRawSubstitutor(method2, classSubstitutor2);
690     if (raw1 ^ raw2) {
691       return raw1 ? Specifics.SECOND : Specifics.FIRST;
692     }
693
694     final boolean varargs1 = info1.isVarargs();
695     final boolean varargs2 = info2.isVarargs();
696     if (varargs1 ^ varargs2) {
697       return varargs1 ? Specifics.SECOND : Specifics.FIRST;
698     }
699
700     return Specifics.NEITHER;
701   }
702
703   private boolean isApplicableTo(@NotNull PsiType[] types2AtSite,
704                                  @NotNull PsiMethod method1,
705                                  @NotNull LanguageLevel languageLevel,
706                                  boolean varargsPosition,
707                                  @NotNull PsiSubstitutor methodSubstitutor1,
708                                  PsiMethod method2) {
709     if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && method2 != null && method1.getTypeParameters().length > 0 && myArgumentsList instanceof PsiExpressionList) {
710       final PsiElement parent = myArgumentsList.getParent();
711       if (parent instanceof PsiCallExpression && ((PsiCallExpression)parent).getTypeArguments().length == 0) {
712         return InferenceSession.isMoreSpecific(method2, method1, ((PsiExpressionList)myArgumentsList).getExpressions(), myArgumentsList, varargsPosition);
713       }
714     }
715     final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method1, methodSubstitutor1, types2AtSite, languageLevel, false, varargsPosition);
716     return applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;
717   }
718
719   @NotNull
720   private static PsiType[] typesAtSite(@NotNull PsiType[] types1, @NotNull PsiSubstitutor siteSubstitutor1) {
721     final PsiType[] types = PsiType.createArray(types1.length);
722     for (int i = 0; i < types1.length; i++) {
723       types[i] = siteSubstitutor1.substitute(types1[i]);
724     }
725     return types;
726   }
727
728   @NotNull
729   private static PsiSubstitutor calculateMethodSubstitutor(@NotNull PsiTypeParameter[] typeParameters,
730                                                            @NotNull PsiMethod method,
731                                                            @NotNull PsiSubstitutor siteSubstitutor,
732                                                            @NotNull PsiType[] types1,
733                                                            @NotNull PsiType[] types2,
734                                                            @NotNull LanguageLevel languageLevel) {
735     PsiSubstitutor substitutor = PsiResolveHelper.SERVICE.getInstance(method.getProject())
736       .inferTypeArguments(typeParameters, types1, types2, languageLevel);
737     for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method)) {
738       ProgressManager.checkCanceled();
739       LOG.assertTrue(typeParameter != null);
740       if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
741         PsiType type = siteSubstitutor.substitute(typeParameter);
742         if (type instanceof PsiClassType && typeParameter.getOwner() == method) {
743           final PsiClass aClass = ((PsiClassType)type).resolve();
744           if (aClass instanceof PsiTypeParameter && ((PsiTypeParameter)aClass).getOwner() == method) {
745             type = TypeConversionUtil.erasure(type, siteSubstitutor);
746           }
747         }
748         substitutor = substitutor.put(typeParameter, type);
749       } else {
750         final PsiType type = substitutor.substitute(typeParameter);
751         if (type instanceof PsiClassType) {
752           final PsiClass aClass = ((PsiClassType)type).resolve();
753           if (aClass instanceof PsiTypeParameter) {
754             substitutor = substitutor.put(typeParameter, JavaPsiFacade.getElementFactory(aClass.getProject()).createType(aClass, siteSubstitutor));
755           }
756         }
757       }
758     }
759     return substitutor;
760   }
761
762   public void checkPrimitiveVarargs(@NotNull List<CandidateInfo> conflicts,
763                                     final int argumentsCount) {
764     if (JavaVersionService.getInstance().isAtLeast(myArgumentsList, JavaSdkVersion.JDK_1_7)) return;
765     CandidateInfo objectVararg = null;
766     for (CandidateInfo conflict : conflicts) {
767       ProgressManager.checkCanceled();
768       final PsiMethod method = (PsiMethod)conflict.getElement();
769       final int parametersCount = method.getParameterList().getParametersCount();
770       if (method.isVarArgs() && parametersCount - 1 == argumentsCount) {
771         final PsiType type = method.getParameterList().getParameters()[parametersCount - 1].getType();
772         final PsiType componentType = ((PsiArrayType)type).getComponentType();
773         final PsiClassType classType = PsiType.getJavaLangObject(method.getManager(), GlobalSearchScope.allScope(method.getProject()));
774         if (Comparing.equal(componentType, classType)) {
775           objectVararg = conflict;
776         }
777       }
778     }
779
780     if (objectVararg != null) {
781       for (CandidateInfo conflict : conflicts) {
782         ProgressManager.checkCanceled();
783         PsiMethod method = (PsiMethod)conflict.getElement();
784         if (method != objectVararg && method.isVarArgs()) {
785           final int paramsCount = method.getParameterList().getParametersCount();
786           final PsiType type = method.getParameterList().getParameters()[paramsCount - 1].getType();
787           final PsiType componentType = ((PsiArrayType)type).getComponentType();
788           if (argumentsCount == paramsCount - 1 && componentType instanceof PsiPrimitiveType) {
789             conflicts.remove(objectVararg);
790             break;
791           }
792         }
793       }
794     }
795   }
796
797   @Nullable
798   private static PsiType getFunctionalType(int functionalTypeIdx, @NotNull CandidateInfo candidateInfo) {
799     final PsiMethod psiMethod = (PsiMethod)candidateInfo.getElement();
800     LOG.assertTrue(true);
801     final PsiParameter[] methodParameters = psiMethod.getParameterList().getParameters();
802     if (methodParameters.length == 0) return null;
803     final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
804     return ((MethodCandidateInfo)candidateInfo).getSiteSubstitutor().substitute(param.getType());
805   }
806
807   @NotNull
808   private static Specifics isFunctionalTypeMoreSpecific(@NotNull CandidateInfo method,
809                                                         @NotNull CandidateInfo conflict,
810                                                         PsiExpression expr,
811                                                         int functionalInterfaceIdx) {
812     if (expr instanceof PsiParenthesizedExpression) {
813       return isFunctionalTypeMoreSpecific(method, conflict, ((PsiParenthesizedExpression)expr).getExpression(), functionalInterfaceIdx);
814     }
815     if (expr instanceof PsiConditionalExpression) {
816       final Specifics thenSpecifics = 
817         isFunctionalTypeMoreSpecific(method, conflict, ((PsiConditionalExpression)expr).getThenExpression(), functionalInterfaceIdx);
818       final Specifics elseSpecifics =
819         isFunctionalTypeMoreSpecific(method, conflict, ((PsiConditionalExpression)expr).getElseExpression(), functionalInterfaceIdx);
820       return thenSpecifics == elseSpecifics ? thenSpecifics : Specifics.NEITHER;
821     }
822
823     if (expr instanceof PsiLambdaExpression || expr instanceof PsiMethodReferenceExpression) {
824
825       if (expr instanceof PsiLambdaExpression && !((PsiLambdaExpression)expr).hasFormalParameterTypes()) {
826         return Specifics.NEITHER;
827       }
828       if (expr instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression)expr).isExact()) {
829         return Specifics.NEITHER;
830       }
831
832       final PsiType sType = getFunctionalType(functionalInterfaceIdx, method);
833       final PsiType tType = getFunctionalType(functionalInterfaceIdx, conflict);
834       if (LambdaUtil.isFunctionalType(sType) && LambdaUtil.isFunctionalType(tType)) {
835         final boolean specific12 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(sType, tType, expr);
836         final boolean specific21 = InferenceSession.isFunctionalTypeMoreSpecificOnExpression(tType, sType, expr);
837         if (specific12 && !specific21) return Specifics.FIRST;
838         if (!specific12 && specific21) return Specifics.SECOND;
839       }
840     }
841     return Specifics.NEITHER;
842   }
843 }