java: prohibit caching when using thread-local types imposed on expressions and decla...
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / infos / MethodCandidateInfo.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.infos;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.openapi.projectRoots.JavaSdkVersion;
20 import com.intellij.openapi.projectRoots.JavaVersionService;
21 import com.intellij.openapi.util.Computable;
22 import com.intellij.openapi.util.RecursionGuard;
23 import com.intellij.openapi.util.RecursionManager;
24 import com.intellij.pom.java.LanguageLevel;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
27 import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
28 import com.intellij.psi.util.PsiTypesUtil;
29 import com.intellij.psi.util.PsiUtil;
30 import com.intellij.psi.util.TypeConversionUtil;
31 import com.intellij.util.ObjectUtils;
32 import com.intellij.util.ThreeState;
33 import org.intellij.lang.annotations.MagicConstant;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import java.util.Arrays;
38 import java.util.Set;
39 import java.util.stream.Collectors;
40
41 /**
42  * @author ik, dsl
43  */
44 public class MethodCandidateInfo extends CandidateInfo{
45   public static final RecursionGuard<PsiElement> ourOverloadGuard = RecursionManager.createGuard("overload.guard");
46   @ApplicabilityLevelConstant private volatile int myApplicabilityLevel;
47   @ApplicabilityLevelConstant private volatile int myPertinentApplicabilityLevel;
48   private final PsiElement myArgumentList;
49   private final PsiType[] myArgumentTypes;
50   private final PsiType[] myTypeArguments;
51   private PsiSubstitutor myCalcedSubstitutor;
52
53   private volatile String myInferenceError;
54   private volatile boolean myApplicabilityError;
55
56   private final LanguageLevel myLanguageLevel;
57   private volatile boolean myErased;
58
59   public MethodCandidateInfo(@NotNull PsiElement candidate,
60                              @NotNull PsiSubstitutor substitutor,
61                              boolean accessProblem,
62                              boolean staticsProblem,
63                              PsiElement argumentList,
64                              PsiElement currFileContext,
65                              @Nullable PsiType[] argumentTypes,
66                              PsiType[] typeArguments) {
67     this(candidate, substitutor, accessProblem, staticsProblem, argumentList, currFileContext, argumentTypes, typeArguments,
68          PsiUtil.getLanguageLevel(argumentList));
69   }
70
71   public MethodCandidateInfo(@NotNull PsiElement candidate,
72                              @NotNull PsiSubstitutor substitutor,
73                              boolean accessProblem,
74                              boolean staticsProblem,
75                              PsiElement argumentList,
76                              PsiElement currFileContext,
77                              @Nullable PsiType[] argumentTypes,
78                              PsiType[] typeArguments,
79                              @NotNull LanguageLevel languageLevel) {
80     super(candidate, substitutor, accessProblem, staticsProblem, currFileContext);
81     myArgumentList = argumentList;
82     myArgumentTypes = argumentTypes;
83     myTypeArguments = typeArguments;
84     myLanguageLevel = languageLevel;
85   }
86
87   /**
88    * To use during overload resolution to choose if method can be applicable by strong/loose invocation.
89    * 
90    * @return true only for java 8+ varargs methods.
91    */
92   public boolean isVarargs() {
93     return false;
94   }
95
96   public boolean isApplicable(){
97     return getPertinentApplicabilityLevel() != ApplicabilityLevel.NOT_APPLICABLE;
98   }
99
100   @ApplicabilityLevelConstant
101   private int getApplicabilityLevelInner() {
102     final PsiType[] argumentTypes = getArgumentTypes();
103
104     if (argumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE;
105
106     int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), argumentTypes, myLanguageLevel);
107     if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable()) level = ApplicabilityLevel.NOT_APPLICABLE;
108     return level;
109   }
110
111
112   @ApplicabilityLevelConstant
113   public int getApplicabilityLevel() {
114     int result = myApplicabilityLevel;
115     if (result == 0) {
116       result = getApplicabilityLevelInner();
117       myApplicabilityLevel = result;
118     }
119     return result;
120   }
121
122   @ApplicabilityLevelConstant
123   public int getPertinentApplicabilityLevel() {
124     int result = myPertinentApplicabilityLevel;
125     if (result == 0) {
126       myPertinentApplicabilityLevel = result = getPertinentApplicabilityLevelInner();
127     }
128     return result;
129   }
130
131   /**
132    * 15.12.2.2 Identify Matching Arity Methods Applicable by Strict Invocation
133    */
134   @ApplicabilityLevelConstant
135   private int getPertinentApplicabilityLevelInner() {
136     if (myArgumentList == null || !myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
137       return getApplicabilityLevel();
138     }
139
140     final PsiMethod method = getElement();
141
142     if (isToInferApplicability()) {
143       //ensure applicability check is performed
144       getSubstitutor(false);
145
146       //already performed checks, so if inference failed, error message should be saved
147       if (myApplicabilityError || isPotentiallyCompatible() != ThreeState.YES) {
148         return ApplicabilityLevel.NOT_APPLICABLE;
149       }
150       return isVarargs() ? ApplicabilityLevel.VARARGS : ApplicabilityLevel.FIXED_ARITY;
151     }
152
153     final PsiSubstitutor substitutor = getSubstitutor(false);
154     final Computable<Integer> computable = () -> computeWithKnownTargetType(() -> {
155       //arg types are calculated here without additional constraints:
156       //non-pertinent to applicability arguments of arguments would be skipped
157       //known target types are cached so poly method calls are able to retrieve that target type when type inference is done
158       //see InferenceSession#getTargetTypeFromParent
159       PsiType[] argumentTypes = getArgumentTypes();
160       if (argumentTypes == null) {
161         return ApplicabilityLevel.NOT_APPLICABLE;
162       }
163
164       int level1 = PsiUtil.getApplicabilityLevel(method, substitutor, argumentTypes, myLanguageLevel, true, true,
165                                                  (left, right, allowUncheckedConversion, argId) -> checkFunctionalInterfaceAcceptance(method, left, right, allowUncheckedConversion));
166       if (!isVarargs() && level1 < ApplicabilityLevel.FIXED_ARITY) {
167         return ApplicabilityLevel.NOT_APPLICABLE;
168       }
169       return level1;
170     }, substitutor);
171     @ApplicabilityLevelConstant int level = ObjectUtils.assertNotNull(ourOverloadGuard.doPreventingRecursion(myArgumentList, false, computable));
172     if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(() -> substitutor)) {
173       level = ApplicabilityLevel.NOT_APPLICABLE;
174     }
175     return level;
176   }
177
178   private <T> T computeWithKnownTargetType(final Computable<T> computable, PsiSubstitutor substitutor) {
179     if (myArgumentList instanceof PsiExpressionList) {
180       PsiExpressionList argumentList = (PsiExpressionList)myArgumentList;
181       PsiExpression[] expressions = Arrays.stream(argumentList.getExpressions())
182         .map(expression -> PsiUtil.skipParenthesizedExprDown(expression))
183         .filter(expression -> expression != null && !(expression instanceof PsiFunctionalExpression))
184         .toArray(PsiExpression[]::new);
185       return ThreadLocalTypes.performWithTypes(expressionTypes -> {
186         PsiMethod method = getElement();
187         boolean varargs = isVarargs();
188         for (PsiExpression context : expressions) {
189           expressionTypes.forceType(context,
190                                     PsiTypesUtil.getTypeByMethod(context, argumentList, method, varargs, substitutor, false));
191         }
192         return computable.compute();
193       });
194     }
195     else {
196       return computable.compute();
197     }
198   }
199  
200   
201   public boolean isOnArgumentList(PsiExpressionList argumentList) {
202     return myArgumentList == argumentList;
203   }
204
205   public void setErased(boolean erased) {
206     myErased = erased;
207   }
208
209   public boolean isErased() {
210     return myErased;
211   }
212
213   private static boolean checkFunctionalInterfaceAcceptance(PsiMethod method, PsiType left, PsiType right, boolean allowUncheckedConversion) {
214     PsiFunctionalExpression fun = null;
215     if (right instanceof PsiLambdaExpressionType) {
216       fun = ((PsiLambdaExpressionType)right).getExpression();
217     }
218     else if (right instanceof PsiMethodReferenceType) {
219       fun = ((PsiMethodReferenceType)right).getExpression();
220     }
221     return fun != null
222            ? !(left instanceof PsiArrayType) && fun.isAcceptable(left, method)
223            : TypeConversionUtil.isAssignable(left, right, allowUncheckedConversion);
224   }
225
226   //If m is a generic method and the method invocation does not provide explicit type
227   //arguments, then the applicability of the method is inferred as specified in p18.5.1
228   public boolean isToInferApplicability() {
229     return myTypeArguments == null && getElement().hasTypeParameters() && !isRawSubstitution();
230   }
231
232   /**
233    * 15.12.2.1 Identify Potentially Applicable Methods
234    */
235   public ThreeState isPotentiallyCompatible() {
236     if (myArgumentList instanceof PsiExpressionList) {
237       final PsiMethod method = getElement();
238       final PsiParameter[] parameters = method.getParameterList().getParameters();
239       final PsiExpression[] expressions = ((PsiExpressionList)myArgumentList).getExpressions();
240
241       if (!isVarargs() &&  myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
242         if (expressions.length != parameters.length) {
243           return ThreeState.NO;
244         }
245       }
246       else {
247         if (expressions.length < parameters.length - 1) {
248           return ThreeState.NO;
249         }
250
251         if (parameters.length == 0 && expressions.length != parameters.length) {
252           return ThreeState.NO;
253         }
254       }
255
256       boolean unsure = false;
257
258       for (int i = 0; i < expressions.length; i++) {
259         final PsiExpression expression = expressions[i];
260         PsiType formalParameterType = i < parameters.length ? parameters[i].getType() : parameters[parameters.length - 1].getType();
261
262         if (formalParameterType instanceof PsiEllipsisType && isVarargs()) {
263           formalParameterType = ((PsiEllipsisType)formalParameterType).getComponentType();
264         }
265
266         ThreeState compatible = isPotentialCompatible(expression, getSiteSubstitutor().substitute(formalParameterType), method);
267         if (compatible == ThreeState.NO) {
268           return ThreeState.NO;
269         }
270
271         if (compatible == ThreeState.UNSURE) {
272           unsure = true;
273         }
274       }
275
276       if (unsure) return ThreeState.UNSURE;
277
278       if (method.hasTypeParameters() && myTypeArguments != null) {
279         return ThreeState.fromBoolean(method.getTypeParameters().length == myTypeArguments.length); //todo
280       }
281     }
282     return ThreeState.YES;
283   }
284
285   private static ThreeState isPotentialCompatible(PsiExpression expression, PsiType formalType, PsiMethod method) {
286     if (expression instanceof PsiFunctionalExpression) {
287       final PsiClass targetTypeParameter = PsiUtil.resolveClassInClassTypeOnly(formalType);
288       if (targetTypeParameter instanceof PsiTypeParameter && method.equals(((PsiTypeParameter)targetTypeParameter).getOwner())) {
289         return ThreeState.YES;
290       }
291
292       if (!LambdaUtil.isFunctionalType(formalType)) {
293         return ThreeState.NO;
294       }
295
296       if (!((PsiFunctionalExpression)expression).isPotentiallyCompatible(formalType)) {
297         return ThreeState.UNSURE;
298       }
299     }
300     else if (expression instanceof PsiParenthesizedExpression) {
301       return isPotentialCompatible(((PsiParenthesizedExpression)expression).getExpression(), formalType, method);
302     }
303     else if (expression instanceof PsiConditionalExpression) {
304       ThreeState thenCompatible = isPotentialCompatible(((PsiConditionalExpression)expression).getThenExpression(), formalType, method);
305       ThreeState elseCompatible = isPotentialCompatible(((PsiConditionalExpression)expression).getElseExpression(), formalType, method);
306       if (thenCompatible == ThreeState.NO || elseCompatible == ThreeState.NO) {
307         return ThreeState.NO;
308       }
309       if (thenCompatible == ThreeState.UNSURE || elseCompatible == ThreeState.UNSURE) {
310         return ThreeState.UNSURE;
311       }
312     }
313     else if (expression instanceof PsiSwitchExpression) {
314       Set<ThreeState> states =
315         PsiUtil.getSwitchResultExpressions((PsiSwitchExpression)expression).stream().map(expr -> isPotentialCompatible(expr, formalType, method)).collect(Collectors.toSet());
316       if (states.contains(ThreeState.NO)) return ThreeState.NO;
317       if (states.contains(ThreeState.UNSURE)) return ThreeState.UNSURE;
318     }
319     return ThreeState.YES;
320   }
321
322   @NotNull
323   public PsiSubstitutor getSiteSubstitutor() {
324     PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
325     if (myTypeArguments != null) {
326       PsiMethod method = getElement();
327       PsiTypeParameter[] typeParams = method.getTypeParameters();
328       for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
329         incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
330       }
331     }
332     return incompleteSubstitutor;
333   }
334
335   @NotNull
336   @Override
337   public PsiSubstitutor getSubstitutor() {
338     return getSubstitutor(true);
339   }
340
341   @NotNull
342   public PsiSubstitutor getSubstitutor(boolean includeReturnConstraint) {
343     PsiSubstitutor substitutor = myCalcedSubstitutor;
344     if (substitutor == null || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || isOverloadCheck()) {
345
346       PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
347       PsiMethod method = getElement();
348       if (myTypeArguments == null) {
349         RecursionGuard.StackStamp stackStamp = RecursionManager.markStack();
350
351         final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint);
352         if (!stackStamp.mayCacheNow() ||
353             isOverloadCheck() ||
354             !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) ||
355             myArgumentList != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(myArgumentList.getParent())
356         ) {
357           return inferredSubstitutor;
358         }
359
360         myCalcedSubstitutor = substitutor = inferredSubstitutor;
361       }
362       else {
363         PsiTypeParameter[] typeParams = method.getTypeParameters();
364         if (isRawSubstitution()) {
365           return JavaPsiFacade.getElementFactory(method.getProject()).createRawSubstitutor(mySubstitutor, typeParams);
366         }
367         for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
368           incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
369         }
370         myCalcedSubstitutor = substitutor = incompleteSubstitutor;
371       }
372     }
373
374     return substitutor;
375   }
376
377   public static boolean isOverloadCheck() {
378     return !ourOverloadGuard.currentStack().isEmpty();
379   }
380
381   public static boolean isOverloadCheck(PsiElement argumentList) {
382     return ourOverloadGuard.currentStack().contains(argumentList);
383   }
384
385   public boolean isTypeArgumentsApplicable() {
386     return isTypeArgumentsApplicable(() -> getSubstitutor(false));
387   }
388
389   private boolean isTypeArgumentsApplicable(Computable<? extends PsiSubstitutor> computable) {
390     final PsiMethod psiMethod = getElement();
391     PsiTypeParameter[] typeParams = psiMethod.getTypeParameters();
392     if (myTypeArguments != null && typeParams.length != myTypeArguments.length && !PsiUtil.isLanguageLevel7OrHigher(psiMethod)){
393       return typeParams.length == 0 && JavaVersionService.getInstance().isAtLeast(psiMethod, JavaSdkVersion.JDK_1_7);
394     }
395     return GenericsUtil.isTypeArgumentsApplicable(typeParams, computable.compute(), getParent());
396   }
397
398   protected PsiElement getParent() {
399     return myArgumentList != null ? myArgumentList.getParent() : null;
400   }
401
402   @Override
403   public boolean isValidResult(){
404     return super.isValidResult() && isApplicable();
405   }
406
407   @NotNull
408   @Override
409   public PsiMethod getElement(){
410     return (PsiMethod)super.getElement();
411   }
412
413   @NotNull
414   public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
415     return inferTypeArguments(policy, myArgumentList instanceof PsiExpressionList
416                                       ? ((PsiExpressionList)myArgumentList).getExpressions()
417                                       : PsiExpression.EMPTY_ARRAY, includeReturnConstraint);
418   }
419
420   public PsiSubstitutor inferSubstitutorFromArgs(@NotNull ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) {
421     if (myTypeArguments == null) {
422       return inferTypeArguments(policy, arguments, true);
423     }
424     return getSiteSubstitutor();
425   }
426
427   /**
428    * If iterated through all candidates, should be called under {@link #ourOverloadGuard} guard so results won't be cached on the top level call
429    */
430   @NotNull
431   public PsiSubstitutor inferTypeArguments(@NotNull final ParameterTypeInferencePolicy policy,
432                                            @NotNull final PsiExpression[] arguments,
433                                            boolean includeReturnConstraint) {
434     final Computable<PsiSubstitutor> computable = () -> {
435       final PsiMethod method = getElement();
436       PsiTypeParameter[] typeParameters = method.getTypeParameters();
437
438       if (isRawSubstitution()) {
439         return JavaPsiFacade.getElementFactory(method.getProject()).createRawSubstitutor(mySubstitutor, typeParameters);
440       }
441
442       final PsiElement parent = getParent();
443       if (parent == null) return PsiSubstitutor.EMPTY;
444       Project project = method.getProject();
445       JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
446       return javaPsiFacade.getResolveHelper()
447         .inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, this, parent, policy,
448                             myLanguageLevel);
449     };
450     PsiSubstitutor substitutor = !includeReturnConstraint
451                                  ? ourOverloadGuard.doPreventingRecursion(myArgumentList, false, computable)
452                                  : computable.compute();
453     return ObjectUtils.assertNotNull(substitutor);
454   }
455
456   public boolean isRawSubstitution() {
457     final PsiMethod method = getElement();
458     if (!method.hasModifierProperty(PsiModifier.STATIC)) {
459       final PsiClass containingClass = method.getContainingClass();
460       if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, mySubstitutor)) {
461         return true;
462       }
463     }
464     return false;
465   }
466
467   public boolean isInferencePossible() {
468     return myArgumentList != null && myArgumentList.isValid();
469   }
470
471
472   @Nullable
473   public PsiType[] getArgumentTypes() {
474     return myArgumentTypes;
475   }
476
477   @Override
478   public boolean equals(Object o) {
479     return super.equals(o) && isVarargs() == ((MethodCandidateInfo)o).isVarargs();
480   }
481
482   @Override
483   public int hashCode() {
484     return 31 * super.hashCode() + (isVarargs() ? 1 : 0);
485   }
486
487   /**
488    * Should be invoked on the top level call expression candidate only
489    */
490   public void setApplicabilityError(@NotNull String applicabilityError) {
491     boolean overloadCheck = isOverloadCheck();
492     if (!overloadCheck) {
493       myInferenceError = applicabilityError;
494     }
495     if (myArgumentList == null ? overloadCheck : isOverloadCheck(myArgumentList)) {
496       markNotApplicable();
497     }
498   }
499
500   public void markNotApplicable() {
501     myApplicabilityError = true;
502   }
503
504   public String getInferenceErrorMessage() {
505     getSubstitutor();
506     return myInferenceError;
507   }
508
509   public String getInferenceErrorMessageAssumeAlreadyComputed() {
510     return myInferenceError;
511   }
512
513   public static class ApplicabilityLevel {
514     public static final int NOT_APPLICABLE = 1;
515     public static final int VARARGS = 2;
516     public static final int FIXED_ARITY = 3;
517   }
518
519   @MagicConstant(intValues = {ApplicabilityLevel.NOT_APPLICABLE, ApplicabilityLevel.VARARGS, ApplicabilityLevel.FIXED_ARITY})
520   public @interface ApplicabilityLevelConstant {
521   }
522 }