new overload resolution: integrate isPotentiallyCompatible in isApplicable checks
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / tree / java / PsiMethodReferenceExpressionImpl.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.impl.source.tree.java;
17
18 import com.intellij.icons.AllIcons;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.Comparing;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.util.registry.Registry;
24 import com.intellij.psi.*;
25 import com.intellij.psi.impl.PsiImplUtil;
26 import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
27 import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
28 import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
29 import com.intellij.psi.impl.source.tree.ChildRole;
30 import com.intellij.psi.impl.source.tree.JavaElementType;
31 import com.intellij.psi.infos.MethodCandidateInfo;
32 import com.intellij.psi.scope.ElementClassFilter;
33 import com.intellij.psi.scope.PsiConflictResolver;
34 import com.intellij.psi.scope.PsiScopeProcessor;
35 import com.intellij.psi.scope.conflictResolvers.DuplicateConflictResolver;
36 import com.intellij.psi.scope.processor.FilterScopeProcessor;
37 import com.intellij.psi.scope.util.PsiScopesUtil;
38 import com.intellij.psi.tree.IElementType;
39 import com.intellij.psi.util.*;
40 import com.intellij.util.IncorrectOperationException;
41 import com.intellij.util.containers.ContainerUtil;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import javax.swing.*;
46 import java.util.*;
47
48 public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression {
49   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl");
50   private static final MethodReferenceResolver RESOLVER = new MethodReferenceResolver();
51
52   public PsiMethodReferenceExpressionImpl() {
53     super(JavaElementType.METHOD_REF_EXPRESSION);
54   }
55
56
57   @Override
58   public PsiTypeElement getQualifierType() {
59     final PsiElement qualifier = getQualifier();
60     return qualifier instanceof PsiTypeElement ? (PsiTypeElement)qualifier : null;
61   }
62
63   @Nullable
64   @Override
65   public PsiType getFunctionalInterfaceType() {
66     return FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(this, true));
67   }
68
69   @Override
70   public boolean isExact() {
71     return getPotentiallyApplicableMember() != null;
72   }
73
74   @Override
75   public boolean isPotentiallyCompatible(final PsiType functionalInterfaceType) {
76     final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
77     if (interfaceMethod == null) return false;
78
79     final MethodReferenceResolver resolver = new MethodReferenceResolver() {
80       @Override
81       protected PsiConflictResolver createResolver(PsiMethodReferenceExpressionImpl referenceExpression,
82                                                    PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
83                                                    PsiMethod interfaceMethod,
84                                                    MethodSignature signature) {
85         return DuplicateConflictResolver.INSTANCE;
86       }
87
88       @Override
89       protected PsiType getInterfaceType(PsiMethodReferenceExpression reference) {
90         return functionalInterfaceType;
91       }
92     };
93
94     final ResolveResult[] result = resolver.resolve(this, getContainingFile(), false);
95     final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(this);
96     final int interfaceArity = interfaceMethod.getParameterList().getParametersCount();
97     for (ResolveResult resolveResult : result) {
98       final PsiElement element = resolveResult.getElement();
99       if (element instanceof PsiMethod) {
100         final boolean isStatic = ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC);
101         final int parametersCount = ((PsiMethod)element).getParameterList().getParametersCount();
102         if (qualifierResolveResult.isReferenceTypeQualified() && getReferenceNameElement() instanceof PsiIdentifier) {
103           if (parametersCount == interfaceArity && isStatic) {
104             return true;
105           }
106           if (parametersCount == interfaceArity - 1 && !isStatic) {
107             return true;
108           }
109           if (((PsiMethod)element).isVarArgs()) return true;
110         }
111         else if (!isStatic) {
112           if (parametersCount == interfaceArity || ((PsiMethod)element).isVarArgs()) {
113             return true;
114           }
115         }
116       } else if (element instanceof PsiClass) {
117         return true;
118       }
119     }
120     return false;
121   }
122
123   @Override
124   public PsiMember getPotentiallyApplicableMember() {
125     return CachedValuesManager.getCachedValue(this, new CachedValueProvider<PsiMember>() {
126       @Nullable
127       @Override
128       public Result<PsiMember> compute() {
129         return Result.createSingleDependency(getPotentiallyApplicableMemberInternal(),
130                                              PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
131       }
132     });
133   }
134
135   private PsiMember getPotentiallyApplicableMemberInternal() {
136     final PsiElement element = getReferenceNameElement();
137     final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(this);
138     final PsiClass containingClass = qualifierResolveResult.getContainingClass();
139     if (containingClass != null) {
140       PsiMethod[] methods = null;
141       if (element instanceof PsiIdentifier) {
142         final String identifierName = element.getText();
143         final List<PsiMethod> result = new ArrayList<PsiMethod>();
144         for (HierarchicalMethodSignature signature : containingClass.getVisibleSignatures()) {
145           if (identifierName.equals(signature.getName())) {
146             result.add(signature.getMethod());
147           }
148         }
149         methods = result.toArray(new PsiMethod[result.size()]);
150       }
151       else if (isConstructor()) {
152         final PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject());
153         final PsiClass arrayClass = factory.getArrayClass(PsiUtil.getLanguageLevel(this));
154         if (arrayClass == containingClass) {
155           final PsiType componentType = qualifierResolveResult.getSubstitutor().substitute(arrayClass.getTypeParameters()[0]);
156           LOG.assertTrue(componentType != null, qualifierResolveResult.getSubstitutor());
157           //15.13.1 A method reference expression of the form ArrayType :: new is always exact.
158           return factory.createMethodFromText("public " + componentType.createArrayType().getCanonicalText() + " __array__(int i) {return null;}", this);
159         } else {
160           methods = containingClass.getConstructors();
161         }
162       }
163       if (methods != null) {
164         PsiMethod psiMethod = null;
165         if (methods.length > 0) {
166           for (PsiMethod method : methods) {
167             if (PsiUtil.isAccessible(method, this, null)) {
168               if (psiMethod != null) return null;
169               psiMethod = method;
170             }
171           }
172           if (psiMethod == null) return null;
173           if (psiMethod.isVarArgs()) return null;
174           if (psiMethod.getTypeParameters().length > 0) {
175             final PsiReferenceParameterList parameterList = getParameterList();
176             return parameterList != null && parameterList.getTypeParameterElements().length > 0 ? psiMethod : null;
177           } else {
178             final PsiSubstitutor classSubstitutor = TypeConversionUtil.getClassSubstitutor(psiMethod.getContainingClass(), containingClass, PsiSubstitutor.EMPTY);
179             final Set<PsiType> signature = new HashSet<PsiType>(Arrays.asList(psiMethod.getSignature(PsiSubstitutor.EMPTY).getParameterTypes()));
180             signature.add(psiMethod.getReturnType());
181             boolean free = true;
182             for (PsiType type : signature) {
183               if (classSubstitutor != null) {
184                 type = classSubstitutor.substitute(type);
185               }
186               if (type != null && PsiPolyExpressionUtil.mentionsTypeParameters(type, ContainerUtil.newHashSet(containingClass.getTypeParameters()))) {
187                 free = false;
188                 break;
189               }
190             }
191             if (free) return psiMethod;
192           }
193         }
194         if (containingClass.hasTypeParameters()) {
195           final PsiElement qualifier = getQualifier();
196           PsiJavaCodeReferenceElement referenceElement = null;
197           if (qualifier instanceof PsiTypeElement) {
198             referenceElement = ((PsiTypeElement)qualifier).getInnermostComponentReferenceElement();
199           } else if (qualifier instanceof PsiReferenceExpression) {
200             final PsiReferenceExpression expression = (PsiReferenceExpression)qualifier;
201             if (qualifierResolveResult.isReferenceTypeQualified()) {
202               referenceElement = expression;
203             }
204           }
205           if (referenceElement != null) {
206             final PsiReferenceParameterList parameterList = referenceElement.getParameterList();
207             if (parameterList == null || parameterList.getTypeParameterElements().length == 0) {
208               return null;
209             }
210           }
211         }
212         return psiMethod == null ? containingClass : psiMethod;
213       }
214     }
215     return null;
216   }
217   
218   @Override
219   public PsiExpression getQualifierExpression() {
220     final PsiElement qualifier = getQualifier();
221     return qualifier instanceof PsiExpression ? (PsiExpression)qualifier : null;
222   }
223
224   @Override
225   public PsiType getType() {
226     return new PsiMethodReferenceType(this);
227   }
228
229   @Override
230   public PsiElement getReferenceNameElement() {
231     final PsiElement element = getLastChild();
232     return element instanceof PsiIdentifier || PsiUtil.isJavaToken(element, JavaTokenType.NEW_KEYWORD) ? element : null;
233   }
234
235   @Override
236   public void processVariants(@NotNull final PsiScopeProcessor processor) {
237     final FilterScopeProcessor proc = new FilterScopeProcessor(ElementClassFilter.METHOD, processor);
238     PsiScopesUtil.resolveAndWalk(proc, this, null, true);
239   }
240
241   @Override
242   public void setQualifierExpression(@Nullable PsiExpression newQualifier) throws IncorrectOperationException {
243     if (newQualifier == null) {
244       LOG.error("Forbidden null qualifier");
245       return;
246     }
247     final PsiExpression expression = getQualifierExpression();
248     if (expression != null) {
249       expression.replace(newQualifier);
250     } else {
251       final PsiElement qualifier = getQualifier();
252       if (qualifier != null) {
253         qualifier.replace(newQualifier);
254       }
255     }
256   }
257
258   @Override
259   public int getChildRole(ASTNode child) {
260     final IElementType elType = child.getElementType();
261     if (elType == JavaTokenType.DOUBLE_COLON) {
262       return ChildRole.DOUBLE_COLON;
263     } else if (elType == JavaTokenType.IDENTIFIER) {
264       return ChildRole.REFERENCE_NAME;
265     } else if (elType == JavaElementType.REFERENCE_EXPRESSION) {
266       return ChildRole.CLASS_REFERENCE;
267     }
268     return ChildRole.EXPRESSION;
269   }
270
271   @NotNull
272   @Override
273   public JavaResolveResult[] multiResolve(boolean incompleteCode) {
274     return PsiImplUtil.multiResolveImpl(this, incompleteCode, RESOLVER);
275   }
276
277   @Override
278   public PsiElement getQualifier() {
279     final PsiElement element = getFirstChild();
280     return element instanceof PsiExpression || element instanceof PsiTypeElement ? element : null;
281   }
282
283   @Override
284   public TextRange getRangeInElement() {
285     final PsiElement element = getReferenceNameElement();
286     if (element != null) {
287       final int offsetInParent = element.getStartOffsetInParent();
288       return new TextRange(offsetInParent, offsetInParent + element.getTextLength());
289     }
290     final PsiElement colons = findPsiChildByType(JavaTokenType.DOUBLE_COLON);
291     if (colons != null) {
292       final int offsetInParent = colons.getStartOffsetInParent();
293       return new TextRange(offsetInParent, offsetInParent + colons.getTextLength());
294     }
295     LOG.error(getText());
296     return null;
297   }
298
299   @NotNull
300   @Override
301   public String getCanonicalText() {
302     return getText();
303   }
304
305   @Override
306   public boolean isReferenceTo(final PsiElement element) {
307     if (!(element instanceof PsiMethod)) return false;
308     final PsiMethod method = (PsiMethod)element;
309
310     final PsiElement nameElement = getReferenceNameElement();
311     if (nameElement instanceof PsiIdentifier) {
312       if (!nameElement.getText().equals(method.getName())) return false;
313     }
314     else if (PsiUtil.isJavaToken(nameElement, JavaTokenType.NEW_KEYWORD)) {
315       if (!method.isConstructor()) return false;
316     }
317
318     return element.getManager().areElementsEquivalent(element, resolve());
319   }
320
321   @Override
322   public void accept(@NotNull final PsiElementVisitor visitor) {
323     if (visitor instanceof JavaElementVisitor) {
324       ((JavaElementVisitor)visitor).visitMethodReferenceExpression(this);
325     }
326     else {
327       visitor.visitElement(this);
328     }
329   }
330
331   @Override
332   public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
333     return this;
334   }
335
336   @Override
337   public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
338     PsiElement oldIdentifier = findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME);
339     if (oldIdentifier == null) {
340       oldIdentifier = findChildByRoleAsPsiElement(ChildRole.CLASS_REFERENCE);
341     }
342     if (oldIdentifier == null) {
343       throw new IncorrectOperationException();
344     }
345     final String oldRefName = oldIdentifier.getText();
346     if (PsiKeyword.THIS.equals(oldRefName) ||
347         PsiKeyword.SUPER.equals(oldRefName) ||
348         PsiKeyword.NEW.equals(oldRefName) ||
349         Comparing.strEqual(oldRefName, newElementName)) {
350       return this;
351     }
352     PsiIdentifier identifier = JavaPsiFacade.getInstance(getProject()).getElementFactory().createIdentifier(newElementName);
353     oldIdentifier.replace(identifier);
354     return this;
355   }
356
357   @Override
358   public boolean isConstructor() {
359     final PsiElement element = getReferenceNameElement();
360     return element instanceof PsiKeyword && PsiKeyword.NEW.equals(element.getText());
361   }
362
363   @Override
364   public String toString() {
365     return "PsiMethodReferenceExpression:" + getText();
366   }
367
368   @Override
369   public boolean isAcceptable(PsiType left) {
370     if (left instanceof PsiIntersectionType) {
371       for (PsiType conjunct : ((PsiIntersectionType)left).getConjuncts()) {
372         if (isAcceptable(conjunct)) return true;
373       }
374       return false;
375     }
376
377     final PsiExpressionList argsList = PsiTreeUtil.getParentOfType(this, PsiExpressionList.class);
378     final boolean isExact = isExact();
379     if (MethodCandidateInfo.ourOverloadGuard.currentStack().contains(argsList)) {
380       final MethodCandidateInfo.CurrentCandidateProperties candidateProperties = MethodCandidateInfo.getCurrentMethod(argsList);
381       if (candidateProperties != null) {
382         final PsiMethod method = candidateProperties.getMethod();
383         if (isExact && !InferenceSession.isPertinentToApplicability(this, method)) {
384           return true;
385         }
386
387         if (LambdaUtil.isPotentiallyCompatibleWithTypeParameter(this, argsList, method)) {
388           return true;
389         }
390       }
391     }
392
393     left = FunctionalInterfaceParameterizationUtil.getGroundTargetType(left);
394     if (!isPotentiallyCompatible(left)) {
395       return false;
396     }
397
398     if (MethodCandidateInfo.ourOverloadGuard.currentStack().contains(argsList)) {
399       if (!isExact) {
400         return true;
401       }
402     }
403
404      // A method reference is congruent with a function type if the following are true:
405      //   The function type identifies a single compile-time declaration corresponding to the reference.
406      //   One of the following is true:
407      //      i)The return type of the function type is void.
408      //     ii)The return type of the function type is R; 
409      //        the result of applying capture conversion (5.1.10) to the return type of the invocation type (15.12.2.6) of the chosen declaration is R', 
410      //        where R is the target type that may be used to infer R'; neither R nor R' is void; and R' is compatible with R in an assignment context.
411
412     Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
413     final JavaResolveResult result;
414     try {
415       if (map.put(this, left) != null) {
416         return false;
417       }
418       result = advancedResolve(false);
419     }
420     finally {
421       map.remove(this);
422     }
423
424     final PsiElement resolve = result.getElement();
425     if (resolve == null) {
426       return false;
427     }
428
429     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(left);
430     final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
431     if (interfaceMethod != null) {
432       final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(left);
433
434       if (interfaceReturnType == PsiType.VOID || interfaceReturnType == null) {
435         return true;
436       }
437
438       PsiSubstitutor subst = result.getSubstitutor();
439
440       PsiType methodReturnType = null;
441       PsiClass containingClass = null;
442       if (resolve instanceof PsiMethod) {
443         containingClass = ((PsiMethod)resolve).getContainingClass();
444
445         PsiType returnType = PsiTypesUtil.patchMethodGetClassReturnType(this, this, (PsiMethod)resolve, null, PsiUtil.getLanguageLevel(this));
446
447         if (returnType == null) {
448           returnType = ((PsiMethod)resolve).getReturnType();
449         }
450
451         if (returnType == PsiType.VOID) {
452           return false;
453         }
454
455         PsiClass qContainingClass = PsiMethodReferenceUtil.getQualifierResolveResult(this).getContainingClass();
456         if (qContainingClass != null && containingClass != null &&
457             PsiMethodReferenceUtil.isReceiverType(left, containingClass, (PsiMethod)resolve)) {
458           subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
459           LOG.assertTrue(subst != null);
460         }
461
462         methodReturnType = subst.substitute(returnType);
463       }
464       else if (resolve instanceof PsiClass) {
465         if (resolve == JavaPsiFacade.getElementFactory(resolve.getProject()).getArrayClass(PsiUtil.getLanguageLevel(resolve))) {
466           final PsiTypeParameter[] typeParameters = ((PsiClass)resolve).getTypeParameters();
467           if (typeParameters.length == 1) {
468             final PsiType arrayComponentType = subst.substitute(typeParameters[0]);
469             if (arrayComponentType == null) {
470               return false;
471             }
472             methodReturnType = arrayComponentType.createArrayType();
473           }
474         }
475         containingClass = (PsiClass)resolve;
476       }
477
478       if (methodReturnType == null) {
479         if (containingClass == null) {
480           return false;
481         }
482         methodReturnType = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass, subst);
483       }
484
485       return TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType, false);
486     }
487     return false;
488   }
489
490   @Nullable
491   @Override
492   public Icon getIcon(int flags) {
493     return AllIcons.Nodes.AnonymousClass;
494   }
495 }