| /* |
| * 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); |
| } |
| } |
| |