2 * Copyright 2000-2014 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.psi.infos;
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;
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;
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));
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;
78 public boolean isVarargs() {
82 public boolean isApplicable(){
83 return getApplicabilityLevel() != ApplicabilityLevel.NOT_APPLICABLE;
86 @ApplicabilityLevelConstant
87 private int getApplicabilityLevelInner() {
88 final PsiType[] argumentTypes = getArgumentTypes();
90 if (argumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE;
92 int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), argumentTypes, myLanguageLevel);
93 if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable()) level = ApplicabilityLevel.NOT_APPLICABLE;
98 @ApplicabilityLevelConstant
99 public int getApplicabilityLevel() {
100 if(myApplicabilityLevel == 0){
101 myApplicabilityLevel = getApplicabilityLevelInner();
103 return myApplicabilityLevel;
106 @ApplicabilityLevelConstant
107 public int getPertinentApplicabilityLevel() {
108 if (myPertinentApplicabilityLevel == 0) {
109 myPertinentApplicabilityLevel = getPertinentApplicabilityLevelInner();
111 return myPertinentApplicabilityLevel;
114 public int getPertinentApplicabilityLevelInner() {
115 if (myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
116 return getApplicabilityLevel();
118 final PsiSubstitutor substitutor = getSubstitutor(false);
119 final PsiMethod method = getElement();
120 @ApplicabilityLevelConstant int level = computeForOverloadedCandidate(new Computable<Integer>() {
122 public Integer compute() {
123 PsiType[] argumentTypes = getArgumentTypes();
124 if (argumentTypes == null) {
125 return ApplicabilityLevel.NOT_APPLICABLE;
128 int level = PsiUtil.getApplicabilityLevel(method, substitutor, argumentTypes, myLanguageLevel);
129 if (!isVarargs() && level < ApplicabilityLevel.FIXED_ARITY) {
130 return ApplicabilityLevel.NOT_APPLICABLE;
135 if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(new Computable<PsiSubstitutor>() {
137 public PsiSubstitutor compute() {
141 level = ApplicabilityLevel.NOT_APPLICABLE;
146 public PsiType[] getPertinentArgumentTypes() {
147 return computeForOverloadedCandidate(new Computable<PsiType[]>() {
148 public PsiType[] compute() {
149 return getArgumentTypes();
151 }, getSubstitutor(false));
154 private <T> T computeForOverloadedCandidate(final Computable<T> computable, final PsiSubstitutor substitutor) {
155 Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
157 map = ContainerUtil.createConcurrentWeakMap();
158 CURRENT_CANDIDATE.set(map);
160 final CurrentCandidateProperties alreadyThere = map.put(getMarkerList(),
161 new CurrentCandidateProperties(getElement(), substitutor, isVarargs(), true));
163 return computable.compute();
166 if (alreadyThere == null) {
167 map.remove(getMarkerList());
169 map.put(getMarkerList(), alreadyThere);
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]);
184 return incompleteSubstitutor;
189 public PsiSubstitutor getSubstitutor() {
190 return getSubstitutor(true);
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();
202 final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint);
204 if (!stackStamp.mayCacheNow() ||
206 !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) ||
207 getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent())) {
208 return inferredSubstitutor;
211 myCalcedSubstitutor = substitutor = inferredSubstitutor;
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]);
218 myCalcedSubstitutor = substitutor = incompleteSubstitutor;
225 public static boolean isOverloadCheck() {
226 return !ourOverloadGuard.currentStack().isEmpty();
230 public boolean isTypeArgumentsApplicable() {
231 return isTypeArgumentsApplicable(new Computable<PsiSubstitutor>() {
233 public PsiSubstitutor compute() {
234 return getSubstitutor(false);
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);
245 return GenericsUtil.isTypeArgumentsApplicable(typeParams, computable.compute(), getParent());
248 protected PsiElement getParent() {
249 return myArgumentList != null ? myArgumentList.getParent() : null;
253 public boolean isValidResult(){
254 return super.isValidResult() && isApplicable();
259 public PsiMethod getElement(){
260 return (PsiMethod)super.getElement();
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);
270 public PsiSubstitutor inferSubstitutorFromArgs(@NotNull ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) {
271 if (myTypeArguments == null) {
272 return inferTypeArguments(policy, arguments, true);
275 return getSiteSubstitutor();
280 public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy,
281 @NotNull PsiExpression[] arguments,
282 boolean includeReturnConstraint) {
283 Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
285 map = ContainerUtil.createConcurrentWeakMap();
286 CURRENT_CANDIDATE.set(map);
288 final PsiMethod method = getElement();
289 final CurrentCandidateProperties alreadyThere =
290 map.put(getMarkerList(), new CurrentCandidateProperties(method, super.getSubstitutor(), policy.isVarargsIgnored() || isVarargs(), !includeReturnConstraint));
292 PsiTypeParameter[] typeParameters = method.getTypeParameters();
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);
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);
311 if (alreadyThere == null) {
312 map.remove(getMarkerList());
314 map.put(getMarkerList(), alreadyThere);
319 protected PsiElement getMarkerList() {
320 return myArgumentList;
323 public boolean isInferencePossible() {
324 return myArgumentList != null && myArgumentList.isValid();
328 public static CurrentCandidateProperties getCurrentMethod(PsiElement context) {
329 final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
330 return currentMethodCandidates != null ? currentMethodCandidates.get(context) : null;
333 public static void updateSubstitutor(PsiElement context, PsiSubstitutor newSubstitutor) {
334 CurrentCandidateProperties candidateProperties = getCurrentMethod(context);
335 if (candidateProperties != null) {
336 candidateProperties.setSubstitutor(newSubstitutor);
340 public PsiType[] getArgumentTypes() {
341 return myArgumentTypes;
345 public boolean equals(Object o) {
346 return super.equals(o) && isVarargs() == ((MethodCandidateInfo)o).isVarargs();
350 public int hashCode() {
351 return 31 * super.hashCode() + (isVarargs() ? 1 : 0);
354 public static class CurrentCandidateProperties {
355 private final PsiMethod myMethod;
356 private PsiSubstitutor mySubstitutor;
357 private boolean myVarargs;
358 private boolean myApplicabilityCheck;
360 public CurrentCandidateProperties(PsiMethod method, PsiSubstitutor substitutor, boolean varargs, boolean applicabilityCheck) {
362 mySubstitutor = substitutor;
364 myApplicabilityCheck = applicabilityCheck;
367 public PsiMethod getMethod() {
371 public PsiSubstitutor getSubstitutor() {
372 return mySubstitutor;
375 public void setSubstitutor(PsiSubstitutor substitutor) {
376 mySubstitutor = substitutor;
379 public boolean isVarargs() {
383 public void setVarargs(boolean varargs) {
387 public boolean isApplicabilityCheck() {
388 return myApplicabilityCheck;
391 public void setApplicabilityCheck(boolean applicabilityCheck) {
392 myApplicabilityCheck = applicabilityCheck;
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;
402 @MagicConstant(intValues = {ApplicabilityLevel.NOT_APPLICABLE, ApplicabilityLevel.VARARGS, ApplicabilityLevel.FIXED_ARITY})
403 public @interface ApplicabilityLevelConstant {