reject caching during resolve only inside lambda body (IDEA-138511)
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / infos / MethodCandidateInfo.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.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.PsiUtil;
29 import com.intellij.util.containers.ContainerUtil;
30 import org.intellij.lang.annotations.MagicConstant;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.util.Map;
35
36 /**
37  * @author ik, dsl
38  */
39 public class MethodCandidateInfo extends CandidateInfo{
40   public static final RecursionGuard ourOverloadGuard = RecursionManager.createGuard("overload.guard");
41   public static final ThreadLocal<Map<PsiElement,  CurrentCandidateProperties>> CURRENT_CANDIDATE = new ThreadLocal<Map<PsiElement, CurrentCandidateProperties>>();
42   @ApplicabilityLevelConstant private int myApplicabilityLevel; // benign race
43   @ApplicabilityLevelConstant private int myPertinentApplicabilityLevel;
44   private final PsiElement myArgumentList;
45   private final PsiType[] myArgumentTypes;
46   private final PsiType[] myTypeArguments;
47   private PsiSubstitutor myCalcedSubstitutor; // benign race
48   private final LanguageLevel myLanguageLevel;
49
50   public MethodCandidateInfo(@NotNull PsiElement candidate,
51                              PsiSubstitutor substitutor,
52                              boolean accessProblem,
53                              boolean staticsProblem,
54                              PsiElement argumentList,
55                              PsiElement currFileContext,
56                              @Nullable PsiType[] argumentTypes,
57                              PsiType[] typeArguments) {
58     this(candidate, substitutor, accessProblem, staticsProblem, argumentList, currFileContext, argumentTypes, typeArguments,
59          PsiUtil.getLanguageLevel(argumentList));
60   }
61
62   public MethodCandidateInfo(@NotNull PsiElement candidate,
63                              @NotNull PsiSubstitutor substitutor,
64                              boolean accessProblem,
65                              boolean staticsProblem,
66                              PsiElement argumentList,
67                              PsiElement currFileContext,
68                              @Nullable PsiType[] argumentTypes,
69                              PsiType[] typeArguments,
70                              @NotNull LanguageLevel languageLevel) {
71     super(candidate, substitutor, accessProblem, staticsProblem, currFileContext);
72     myArgumentList = argumentList;
73     myArgumentTypes = argumentTypes;
74     myTypeArguments = typeArguments;
75     myLanguageLevel = languageLevel;
76   }
77
78   public boolean isVarargs() {
79     return false;
80   }
81
82   public boolean isApplicable(){
83     return getApplicabilityLevel() != ApplicabilityLevel.NOT_APPLICABLE;
84   }
85
86   @ApplicabilityLevelConstant
87   private int getApplicabilityLevelInner() {
88     final PsiType[] argumentTypes = getArgumentTypes();
89
90     if (argumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE;
91
92     int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), argumentTypes, myLanguageLevel);
93     if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable()) level = ApplicabilityLevel.NOT_APPLICABLE;
94     return level;
95   }
96
97
98   @ApplicabilityLevelConstant
99   public int getApplicabilityLevel() {
100     if(myApplicabilityLevel == 0){
101       myApplicabilityLevel = getApplicabilityLevelInner();
102     }
103     return myApplicabilityLevel;
104   }
105
106   @ApplicabilityLevelConstant
107   public int getPertinentApplicabilityLevel() {
108     if (myPertinentApplicabilityLevel == 0) {
109       myPertinentApplicabilityLevel = getPertinentApplicabilityLevelInner();
110     }
111     return myPertinentApplicabilityLevel;
112   }
113   
114   public int getPertinentApplicabilityLevelInner() {
115     if (myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
116       return getApplicabilityLevel();
117     }
118     final PsiSubstitutor substitutor = getSubstitutor(false);
119     final PsiMethod method = getElement();
120     @ApplicabilityLevelConstant int level = computeForOverloadedCandidate(new Computable<Integer>() {
121       @Override
122       public Integer compute() {
123         PsiType[] argumentTypes = getArgumentTypes();
124         if (argumentTypes == null) {
125           return ApplicabilityLevel.NOT_APPLICABLE;
126         }
127
128         int level = PsiUtil.getApplicabilityLevel(method, substitutor, argumentTypes, myLanguageLevel);
129         if (!isVarargs() && level < ApplicabilityLevel.FIXED_ARITY) {
130           return ApplicabilityLevel.NOT_APPLICABLE;
131         }
132         return level;
133       }
134     }, substitutor);
135     if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(new Computable<PsiSubstitutor>() {
136       @Override
137       public PsiSubstitutor compute() {
138         return substitutor;
139       }
140     })) {
141       level = ApplicabilityLevel.NOT_APPLICABLE;
142     }
143     return level;
144   }
145
146   public PsiType[] getPertinentArgumentTypes() {
147     return computeForOverloadedCandidate(new Computable<PsiType[]>() {
148       public PsiType[] compute() {
149         return getArgumentTypes();
150       }
151     }, getSubstitutor(false));
152   }
153
154   private <T> T computeForOverloadedCandidate(final Computable<T> computable, final PsiSubstitutor substitutor) {
155     Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
156     if (map == null) {
157       map = ContainerUtil.createConcurrentWeakMap();
158       CURRENT_CANDIDATE.set(map);
159     }
160     final CurrentCandidateProperties alreadyThere = map.put(getMarkerList(),
161                                                             new CurrentCandidateProperties(getElement(), substitutor, isVarargs(), true));
162     try {
163       return computable.compute();
164     }
165     finally {
166       if (alreadyThere == null) {
167         map.remove(getMarkerList());
168       } else {
169         map.put(getMarkerList(), alreadyThere);
170       }
171     }
172   }
173
174   @NotNull
175   public PsiSubstitutor getSiteSubstitutor() {
176     PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
177     if (myTypeArguments != null) {
178       PsiMethod method = getElement();
179       PsiTypeParameter[] typeParams = method.getTypeParameters();
180       for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
181         incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
182       }
183     }
184     return incompleteSubstitutor;
185   }
186   
187   @NotNull
188   @Override
189   public PsiSubstitutor getSubstitutor() {
190     return getSubstitutor(true);
191   }
192
193   @NotNull
194   public PsiSubstitutor getSubstitutor(boolean includeReturnConstraint) {
195     PsiSubstitutor substitutor = myCalcedSubstitutor;
196     if (substitutor == null || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || isOverloadCheck()) {
197       PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
198       PsiMethod method = getElement();
199       if (myTypeArguments == null) {
200         final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack();
201
202         final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint);
203
204          if (!stackStamp.mayCacheNow() ||
205              isOverloadCheck() ||
206              !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) ||
207              getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent())) {
208           return inferredSubstitutor;
209         }
210
211         myCalcedSubstitutor = substitutor = inferredSubstitutor;
212       }
213       else {
214         PsiTypeParameter[] typeParams = method.getTypeParameters();
215         for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
216           incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
217         }
218         myCalcedSubstitutor = substitutor = incompleteSubstitutor;
219       }
220     }
221
222     return substitutor;
223   }
224
225   public static boolean isOverloadCheck() {
226     return !ourOverloadGuard.currentStack().isEmpty();
227   }
228
229
230   public boolean isTypeArgumentsApplicable() {
231     return isTypeArgumentsApplicable(new Computable<PsiSubstitutor>() {
232       @Override
233       public PsiSubstitutor compute() {
234         return getSubstitutor(false);
235       }
236     });
237   }
238
239   private boolean isTypeArgumentsApplicable(Computable<PsiSubstitutor> computable) {
240     final PsiMethod psiMethod = getElement();
241     PsiTypeParameter[] typeParams = psiMethod.getTypeParameters();
242     if (myTypeArguments != null && typeParams.length != myTypeArguments.length && !PsiUtil.isLanguageLevel7OrHigher(psiMethod)){
243       return typeParams.length == 0 && JavaVersionService.getInstance().isAtLeast(psiMethod, JavaSdkVersion.JDK_1_7);
244     }
245     return GenericsUtil.isTypeArgumentsApplicable(typeParams, computable.compute(), getParent());
246   }
247
248   protected PsiElement getParent() {
249     return myArgumentList != null ? myArgumentList.getParent() : null;
250   }
251
252   @Override
253   public boolean isValidResult(){
254     return super.isValidResult() && isApplicable();
255   }
256
257   @NotNull
258   @Override
259   public PsiMethod getElement(){
260     return (PsiMethod)super.getElement();
261   }
262
263   @NotNull
264   public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
265     return inferTypeArguments(policy, myArgumentList instanceof PsiExpressionList
266                                       ? ((PsiExpressionList)myArgumentList).getExpressions()
267                                       : PsiExpression.EMPTY_ARRAY, includeReturnConstraint);
268   }
269
270   public PsiSubstitutor inferSubstitutorFromArgs(@NotNull ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) {
271     if (myTypeArguments == null) {
272       return inferTypeArguments(policy, arguments, true);
273     }
274     else {
275       return getSiteSubstitutor();
276     }
277   }
278
279   @NotNull
280   public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy,
281                                            @NotNull PsiExpression[] arguments, 
282                                            boolean includeReturnConstraint) {
283     Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
284     if (map == null) {
285       map = ContainerUtil.createConcurrentWeakMap();
286       CURRENT_CANDIDATE.set(map);
287     }
288     final PsiMethod method = getElement();
289     final CurrentCandidateProperties alreadyThere = 
290       map.put(getMarkerList(), new CurrentCandidateProperties(method, super.getSubstitutor(), policy.isVarargsIgnored() || isVarargs(), !includeReturnConstraint));
291     try {
292       PsiTypeParameter[] typeParameters = method.getTypeParameters();
293
294       if (!method.hasModifierProperty(PsiModifier.STATIC)) {
295         final PsiClass containingClass = method.getContainingClass();
296         if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, mySubstitutor)) {
297           Project project = containingClass.getProject();
298           JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
299           return javaPsiFacade.getElementFactory().createRawSubstitutor(mySubstitutor, typeParameters);
300         }
301       }
302
303       final PsiElement parent = getParent();
304       if (parent == null) return PsiSubstitutor.EMPTY;
305       Project project = method.getProject();
306       JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
307       return javaPsiFacade.getResolveHelper()
308         .inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, mySubstitutor, parent, policy, myLanguageLevel);
309     }
310     finally {
311       if (alreadyThere == null) {
312         map.remove(getMarkerList());
313       } else {
314         map.put(getMarkerList(), alreadyThere);
315       }
316     }
317   }
318
319   protected PsiElement getMarkerList() {
320     return myArgumentList;
321   }
322
323   public boolean isInferencePossible() {
324     return myArgumentList != null && myArgumentList.isValid();
325   }
326
327
328   public static CurrentCandidateProperties getCurrentMethod(PsiElement context) {
329     final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
330     return currentMethodCandidates != null ? currentMethodCandidates.get(context) : null;
331   }
332
333   public static void updateSubstitutor(PsiElement context, PsiSubstitutor newSubstitutor) {
334     CurrentCandidateProperties candidateProperties = getCurrentMethod(context);
335     if (candidateProperties != null) {
336       candidateProperties.setSubstitutor(newSubstitutor);
337     }
338   }
339
340   public PsiType[] getArgumentTypes() {
341     return myArgumentTypes;
342   }
343
344   @Override
345   public boolean equals(Object o) {
346     return super.equals(o) && isVarargs() == ((MethodCandidateInfo)o).isVarargs();
347   }
348
349   @Override
350   public int hashCode() {
351     return 31 * super.hashCode() + (isVarargs() ? 1 : 0);
352   }
353
354   public static class CurrentCandidateProperties {
355     private final PsiMethod myMethod;
356     private PsiSubstitutor mySubstitutor;
357     private boolean myVarargs;
358     private boolean myApplicabilityCheck;
359
360     public CurrentCandidateProperties(PsiMethod method, PsiSubstitutor substitutor, boolean varargs, boolean applicabilityCheck) {
361       myMethod = method;
362       mySubstitutor = substitutor;
363       myVarargs = varargs;
364       myApplicabilityCheck = applicabilityCheck;
365     }
366
367     public PsiMethod getMethod() {
368       return myMethod;
369     }
370
371     public PsiSubstitutor getSubstitutor() {
372       return mySubstitutor;
373     }
374
375     public void setSubstitutor(PsiSubstitutor substitutor) {
376       mySubstitutor = substitutor;
377     }
378
379     public boolean isVarargs() {
380       return myVarargs;
381     }
382
383     public void setVarargs(boolean varargs) {
384       myVarargs = varargs;
385     }
386
387     public boolean isApplicabilityCheck() {
388       return myApplicabilityCheck;
389     }
390
391     public void setApplicabilityCheck(boolean applicabilityCheck) {
392       myApplicabilityCheck = applicabilityCheck;
393     }
394   }
395   
396   public static class ApplicabilityLevel {
397     public static final int NOT_APPLICABLE = 1;
398     public static final int VARARGS = 2;
399     public static final int FIXED_ARITY = 3;
400   }
401
402   @MagicConstant(intValues = {ApplicabilityLevel.NOT_APPLICABLE, ApplicabilityLevel.VARARGS, ApplicabilityLevel.FIXED_ARITY})
403   public @interface ApplicabilityLevelConstant {
404   }
405 }