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