blob: 1a5d2d14dc0a39a5cf8901b8e8ff04cf1e7188a6 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.infos;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ConcurrentWeakHashMap;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author ik, dsl
*/
public class MethodCandidateInfo extends CandidateInfo{
public static final RecursionGuard ourOverloadGuard = RecursionManager.createGuard("overload.guard");
public static final ThreadLocal<Map<PsiElement, CurrentCandidateProperties>> CURRENT_CANDIDATE = new ThreadLocal<Map<PsiElement, CurrentCandidateProperties>>();
@ApplicabilityLevelConstant private int myApplicabilityLevel; // benign race
@ApplicabilityLevelConstant private int myPertinentApplicabilityLevel;
private final PsiElement myArgumentList;
private final PsiType[] myArgumentTypes;
private final PsiType[] myTypeArguments;
private PsiSubstitutor myCalcedSubstitutor; // benign race
private final LanguageLevel myLanguageLevel;
public MethodCandidateInfo(@NotNull PsiElement candidate,
PsiSubstitutor substitutor,
boolean accessProblem,
boolean staticsProblem,
PsiElement argumentList,
PsiElement currFileContext,
@Nullable PsiType[] argumentTypes,
PsiType[] typeArguments) {
this(candidate, substitutor, accessProblem, staticsProblem, argumentList, currFileContext, argumentTypes, typeArguments,
PsiUtil.getLanguageLevel(argumentList));
}
public MethodCandidateInfo(@NotNull PsiElement candidate,
@NotNull PsiSubstitutor substitutor,
boolean accessProblem,
boolean staticsProblem,
PsiElement argumentList,
PsiElement currFileContext,
@Nullable PsiType[] argumentTypes,
PsiType[] typeArguments,
@NotNull LanguageLevel languageLevel) {
super(candidate, substitutor, accessProblem, staticsProblem, currFileContext);
myArgumentList = argumentList;
myArgumentTypes = argumentTypes;
myTypeArguments = typeArguments;
myLanguageLevel = languageLevel;
}
public boolean isVarargs() {
return false;
}
public boolean isApplicable(){
return getApplicabilityLevel() != ApplicabilityLevel.NOT_APPLICABLE;
}
@ApplicabilityLevelConstant
private int getApplicabilityLevelInner() {
final PsiType[] argumentTypes = getArgumentTypes();
if (argumentTypes == null) return ApplicabilityLevel.NOT_APPLICABLE;
int level = PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(), argumentTypes, myLanguageLevel);
if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable()) level = ApplicabilityLevel.NOT_APPLICABLE;
return level;
}
@ApplicabilityLevelConstant
public int getApplicabilityLevel() {
if(myApplicabilityLevel == 0){
myApplicabilityLevel = getApplicabilityLevelInner();
}
return myApplicabilityLevel;
}
@ApplicabilityLevelConstant
public int getPertinentApplicabilityLevel() {
if (myPertinentApplicabilityLevel == 0) {
myPertinentApplicabilityLevel = getPertinentApplicabilityLevelInner();
}
return myPertinentApplicabilityLevel;
}
public int getPertinentApplicabilityLevelInner() {
if (myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
return getApplicabilityLevel();
}
@ApplicabilityLevelConstant int level;
Integer boxedLevel = ourOverloadGuard.doPreventingRecursion(myArgumentList, false, new Computable<Integer>() {
@Override
public Integer compute() {
if (PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
PsiSubstitutor substitutor = getSubstitutor(false);
Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
if (map == null) {
map = new ConcurrentWeakHashMap<PsiElement, CurrentCandidateProperties>();
CURRENT_CANDIDATE.set(map);
}
final PsiMethod method = getElement();
final CurrentCandidateProperties properties = new CurrentCandidateProperties(method, substitutor, isVarargs(), true);
final CurrentCandidateProperties alreadyThere = map.put(getMarkerList(), properties);
try {
PsiType[] argumentTypes = getArgumentTypes();
if (argumentTypes == null) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method, substitutor, argumentTypes, myLanguageLevel);
if (!isVarargs() && applicabilityLevel < ApplicabilityLevel.FIXED_ARITY) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
return applicabilityLevel;
}
finally {
if (alreadyThere == null) {
map.remove(getMarkerList());
} else {
map.put(getMarkerList(), alreadyThere);
}
}
}
return getApplicabilityLevelInner();
}
});
if (boxedLevel == null) {
return getApplicabilityLevel();
}
level = boxedLevel;
if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(false)) level = ApplicabilityLevel.NOT_APPLICABLE;
return level;
}
@NotNull
public PsiSubstitutor getSiteSubstitutor() {
PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
if (myTypeArguments != null) {
PsiMethod method = getElement();
PsiTypeParameter[] typeParams = method.getTypeParameters();
for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
}
}
return incompleteSubstitutor;
}
@NotNull
@Override
public PsiSubstitutor getSubstitutor() {
return getSubstitutor(true);
}
@NotNull
public PsiSubstitutor getSubstitutor(boolean includeReturnConstraint) {
PsiSubstitutor substitutor = myCalcedSubstitutor;
if (substitutor == null || !includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) || isOverloadCheck()) {
PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
PsiMethod method = getElement();
if (myTypeArguments == null) {
final RecursionGuard.StackStamp stackStamp = PsiDiamondType.ourDiamondGuard.markStack();
final PsiSubstitutor inferredSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE, includeReturnConstraint);
if (!stackStamp.mayCacheNow() ||
isOverloadCheck() ||
!includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) ||
getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent())) {
return inferredSubstitutor;
}
myCalcedSubstitutor = substitutor = inferredSubstitutor;
}
else {
PsiTypeParameter[] typeParams = method.getTypeParameters();
for (int i = 0; i < myTypeArguments.length && i < typeParams.length; i++) {
incompleteSubstitutor = incompleteSubstitutor.put(typeParams[i], myTypeArguments[i]);
}
myCalcedSubstitutor = substitutor = incompleteSubstitutor;
}
}
return substitutor;
}
public static boolean isOverloadCheck() {
return !ourOverloadGuard.currentStack().isEmpty();
}
public boolean isTypeArgumentsApplicable() {
return isTypeArgumentsApplicable(false);
}
public boolean isTypeArgumentsApplicable(boolean includeReturnConstraint) {
final PsiMethod psiMethod = getElement();
PsiTypeParameter[] typeParams = psiMethod.getTypeParameters();
if (myTypeArguments != null && typeParams.length != myTypeArguments.length && !PsiUtil.isLanguageLevel7OrHigher(psiMethod)){
return typeParams.length == 0 && JavaVersionService.getInstance().isAtLeast(psiMethod, JavaSdkVersion.JDK_1_7);
}
PsiSubstitutor substitutor = getSubstitutor(includeReturnConstraint);
return GenericsUtil.isTypeArgumentsApplicable(typeParams, substitutor, getParent());
}
protected PsiElement getParent() {
return myArgumentList != null ? myArgumentList.getParent() : null;
}
@Override
public boolean isValidResult(){
return super.isValidResult() && isApplicable();
}
@NotNull
@Override
public PsiMethod getElement(){
return (PsiMethod)super.getElement();
}
@NotNull
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
return inferTypeArguments(policy, myArgumentList instanceof PsiExpressionList
? ((PsiExpressionList)myArgumentList).getExpressions()
: PsiExpression.EMPTY_ARRAY, includeReturnConstraint);
}
public PsiSubstitutor inferSubstitutorFromArgs(@NotNull ParameterTypeInferencePolicy policy, final PsiExpression[] arguments) {
if (myTypeArguments == null) {
return inferTypeArguments(policy, arguments, true);
}
else {
return getSiteSubstitutor();
}
}
@NotNull
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy,
@NotNull PsiExpression[] arguments,
boolean includeReturnConstraint) {
Map<PsiElement, CurrentCandidateProperties> map = CURRENT_CANDIDATE.get();
if (map == null) {
map = new ConcurrentWeakHashMap<PsiElement, CurrentCandidateProperties>();
CURRENT_CANDIDATE.set(map);
}
final PsiMethod method = getElement();
final CurrentCandidateProperties alreadyThere =
map.put(getMarkerList(), new CurrentCandidateProperties(method, super.getSubstitutor(), policy.isVarargsIgnored() || isVarargs(), !includeReturnConstraint));
try {
PsiTypeParameter[] typeParameters = method.getTypeParameters();
if (!method.hasModifierProperty(PsiModifier.STATIC)) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, mySubstitutor)) {
Project project = containingClass.getProject();
JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
return javaPsiFacade.getElementFactory().createRawSubstitutor(mySubstitutor, typeParameters);
}
}
final PsiElement parent = getParent();
if (parent == null) return PsiSubstitutor.EMPTY;
Project project = method.getProject();
JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
return javaPsiFacade.getResolveHelper()
.inferTypeArguments(typeParameters, method.getParameterList().getParameters(), arguments, mySubstitutor, parent, policy, myLanguageLevel);
}
finally {
if (alreadyThere == null) {
map.remove(getMarkerList());
} else {
map.put(getMarkerList(), alreadyThere);
}
}
}
protected PsiElement getMarkerList() {
return myArgumentList;
}
public boolean isInferencePossible() {
return myArgumentList != null && myArgumentList.isValid();
}
public static CurrentCandidateProperties getCurrentMethod(PsiElement context) {
final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
return currentMethodCandidates != null ? currentMethodCandidates.get(context) : null;
}
public static void updateSubstitutor(PsiElement context, PsiSubstitutor newSubstitutor) {
final Map<PsiElement, CurrentCandidateProperties> currentMethodCandidates = CURRENT_CANDIDATE.get();
if (currentMethodCandidates != null) {
final CurrentCandidateProperties properties = currentMethodCandidates.get(context);
if (properties != null) {
properties.setSubstitutor(newSubstitutor);
}
}
}
public PsiType[] getArgumentTypes() {
return myArgumentTypes;
}
@Override
public boolean equals(Object o) {
return super.equals(o) && isVarargs() == ((MethodCandidateInfo)o).isVarargs();
}
@Override
public int hashCode() {
return 31 * super.hashCode() + (isVarargs() ? 1 : 0);
}
public static class CurrentCandidateProperties {
private final PsiMethod myMethod;
private PsiSubstitutor mySubstitutor;
private boolean myVarargs;
private boolean myApplicabilityCheck;
public CurrentCandidateProperties(PsiMethod method, PsiSubstitutor substitutor, boolean varargs, boolean applicabilityCheck) {
myMethod = method;
mySubstitutor = substitutor;
myVarargs = varargs;
myApplicabilityCheck = applicabilityCheck;
}
public PsiMethod getMethod() {
return myMethod;
}
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
public void setSubstitutor(PsiSubstitutor substitutor) {
mySubstitutor = substitutor;
}
public boolean isVarargs() {
return myVarargs;
}
public void setVarargs(boolean varargs) {
myVarargs = varargs;
}
public boolean isApplicabilityCheck() {
return myApplicabilityCheck;
}
public void setApplicabilityCheck(boolean applicabilityCheck) {
myApplicabilityCheck = applicabilityCheck;
}
}
public static class ApplicabilityLevel {
public static final int NOT_APPLICABLE = 1;
public static final int VARARGS = 2;
public static final int FIXED_ARITY = 3;
}
@MagicConstant(intValues = {ApplicabilityLevel.NOT_APPLICABLE, ApplicabilityLevel.VARARGS, ApplicabilityLevel.FIXED_ARITY})
public @interface ApplicabilityLevelConstant {
}
}