blob: dc204b4ae3159da27911e2e732bc391165074bdc [file] [log] [blame]
/*
* Copyright 2000-2009 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.impl.source.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.resolve.JavaResolveCache;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.ChildRoleBase;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PsiMethodCallExpressionImpl extends ExpressionPsiElement implements PsiMethodCallExpression {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl");
public PsiMethodCallExpressionImpl() {
super(JavaElementType.METHOD_CALL_EXPRESSION);
}
@Override
public PsiType getType() {
return JavaResolveCache.getInstance(getProject()).getType(this, ourTypeEvaluator);
}
@Override
public PsiMethod resolveMethod() {
return (PsiMethod)getMethodExpression().resolve();
}
@Override
@NotNull
public JavaResolveResult resolveMethodGenerics() {
return getMethodExpression().advancedResolve(false);
}
@Override
public void removeChild(@NotNull ASTNode child) {
if (child == getArgumentList()) {
LOG.error("Cannot delete argument list since it will break contract on argument list notnullity");
}
super.removeChild(child);
}
@Override
@NotNull
public PsiReferenceParameterList getTypeArgumentList() {
PsiReferenceExpression expression = getMethodExpression();
PsiReferenceParameterList result = expression.getParameterList();
if (result != null) return result;
LOG.error("Invalid method call expression. Children:\n" + DebugUtil.psiTreeToString(expression, false));
return result;
}
@Override
@NotNull
public PsiType[] getTypeArguments() {
return getMethodExpression().getTypeParameters();
}
@Override
@NotNull
public PsiReferenceExpression getMethodExpression() {
return (PsiReferenceExpression)findChildByRoleAsPsiElement(ChildRole.METHOD_EXPRESSION);
}
@Override
@NotNull
public PsiExpressionList getArgumentList() {
PsiExpressionList list = (PsiExpressionList)findChildByRoleAsPsiElement(ChildRole.ARGUMENT_LIST);
if (list != null) return list;
LOG.error("Invalid PSI. Children:" + DebugUtil.psiToString(this, false));
return list;
}
@Override
public ASTNode findChildByRole(int role) {
LOG.assertTrue(ChildRole.isUnique(role));
switch (role) {
default:
return null;
case ChildRole.METHOD_EXPRESSION:
return getFirstChildNode();
case ChildRole.ARGUMENT_LIST:
return findChildByType(JavaElementType.EXPRESSION_LIST);
}
}
@Override
public int getChildRole(ASTNode child) {
LOG.assertTrue(child.getTreeParent() == this);
IElementType i = child.getElementType();
if (i == JavaElementType.EXPRESSION_LIST) {
return ChildRole.ARGUMENT_LIST;
}
else {
if (ElementType.EXPRESSION_BIT_SET.contains(child.getElementType())) {
return ChildRole.METHOD_EXPRESSION;
}
return ChildRoleBase.NONE;
}
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitMethodCallExpression(this);
}
else {
visitor.visitElement(this);
}
}
public String toString() {
return "PsiMethodCallExpression:" + getText();
}
private static final TypeEvaluator ourTypeEvaluator = new TypeEvaluator();
private static class TypeEvaluator implements Function<PsiMethodCallExpression, PsiType> {
@Override
@Nullable
public PsiType fun(final PsiMethodCallExpression call) {
PsiReferenceExpression methodExpression = call.getMethodExpression();
PsiType theOnly = null;
final JavaResolveResult[] results = methodExpression.multiResolve(false);
LanguageLevel languageLevel = PsiUtil.getLanguageLevel(call);
for (int i = 0; i < results.length; i++) {
final PsiType type = getResultType(call, methodExpression, results[i], languageLevel);
if (type == null) {
return null;
}
if (i == 0) {
theOnly = type;
}
else if (!theOnly.equals(type)) {
return null;
}
}
return theOnly;
}
@Nullable
private static PsiType getResultType(PsiMethodCallExpression call,
PsiReferenceExpression methodExpression,
JavaResolveResult result,
@NotNull final LanguageLevel languageLevel) {
final PsiMethod method = (PsiMethod)result.getElement();
if (method == null) return null;
boolean is15OrHigher = languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0;
final PsiType getClassReturnType = PsiTypesUtil.patchMethodGetClassReturnType(call, methodExpression, method,
new Condition<IElementType>() {
@Override
public boolean value(IElementType type) {
return type != JavaElementType.CLASS;
}
}, languageLevel);
if (getClassReturnType != null) {
return getClassReturnType;
}
PsiType ret = method.getReturnType();
if (ret == null) return null;
if (ret instanceof PsiClassType) {
ret = ((PsiClassType)ret).setLanguageLevel(languageLevel);
}
if (is15OrHigher) {
return captureReturnType(call, method, ret, result.getSubstitutor());
}
return TypeConversionUtil.erasure(ret);
}
}
public static PsiType captureReturnType(PsiMethodCallExpression call,
PsiMethod method,
PsiType ret,
PsiSubstitutor substitutor) {
PsiType substitutedReturnType = substitutor.substitute(ret);
if (substitutedReturnType == null) return TypeConversionUtil.erasure(ret);
if (InferenceSession.wasUncheckedConversionPerformed(call)) {
// 18.5.2
// if unchecked conversion was necessary, then this substitution provides the parameter types of the invocation type,
// while the return type and thrown types are given by the erasure of m's type (without applying θ').
return TypeConversionUtil.erasure(substitutedReturnType);
}
if (PsiUtil.isRawSubstitutor(method, substitutor)) {
final PsiType returnTypeErasure = TypeConversionUtil.erasure(ret);
if (Comparing.equal(TypeConversionUtil.erasure(substitutedReturnType), returnTypeErasure)) {
return returnTypeErasure;
}
}
PsiType lowerBound = PsiType.NULL;
if (substitutedReturnType instanceof PsiCapturedWildcardType) {
lowerBound = ((PsiCapturedWildcardType)substitutedReturnType).getLowerBound();
} else if (substitutedReturnType instanceof PsiWildcardType) {
lowerBound = ((PsiWildcardType)substitutedReturnType).getSuperBound();
}
if (lowerBound != PsiType.NULL) { //? super
final PsiClass containingClass = method.getContainingClass();
final PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
final PsiClass childClass = qualifierExpression != null ? PsiUtil.resolveClassInClassTypeOnly(qualifierExpression.getType()) : null;
if (containingClass != null && childClass != null) {
final PsiType typeInChildClassTypeParams = TypeConversionUtil.getSuperClassSubstitutor(containingClass, childClass, PsiSubstitutor.EMPTY).substitute(ret);
final PsiClass substituted = PsiUtil.resolveClassInClassTypeOnly(typeInChildClassTypeParams);
if (substituted instanceof PsiTypeParameter) {
final PsiClassType[] extendsListTypes = substituted.getExtendsListTypes();
if (extendsListTypes.length == 1) {
return extendsListTypes[0];
}
}
}
}
return PsiImplUtil.normalizeWildcardTypeByPosition(substitutedReturnType, call);
}
}