| /* |
| * Copyright 2000-2013 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.resolve.graphInference; |
| |
| import com.intellij.psi.*; |
| import com.intellij.psi.infos.MethodCandidateInfo; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * User: anna |
| */ |
| public class PsiPolyExpressionUtil { |
| public static boolean hasStandaloneForm(PsiExpression expression) { |
| if (expression instanceof PsiLambdaExpression || |
| expression instanceof PsiMethodReferenceExpression || |
| expression instanceof PsiParenthesizedExpression || |
| expression instanceof PsiConditionalExpression || |
| expression instanceof PsiCallExpression) { |
| return false; |
| } |
| return true; |
| } |
| |
| public static boolean isPolyExpression(final PsiExpression expression) { |
| if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) { |
| return true; |
| } |
| else if (expression instanceof PsiParenthesizedExpression) { |
| return isPolyExpression(((PsiParenthesizedExpression)expression).getExpression()); |
| } |
| else if (expression instanceof PsiNewExpression) { |
| final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expression).getClassReference(); |
| if (classReference != null) { |
| final PsiReferenceParameterList parameterList = classReference.getParameterList(); |
| if (parameterList != null) { |
| final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements(); |
| if (typeElements.length == 1 && typeElements[0].getType() instanceof PsiDiamondType) { |
| return isInAssignmentOrInvocationContext(expression); |
| } |
| } |
| } |
| } else if (expression instanceof PsiMethodCallExpression) { |
| final MethodCandidateInfo.CurrentCandidateProperties candidateProperties = MethodCandidateInfo.getCurrentMethod(((PsiMethodCallExpression)expression).getArgumentList()); |
| return isMethodCallPolyExpression(expression, candidateProperties != null ? candidateProperties.getMethod() : ((PsiMethodCallExpression)expression).resolveMethod()); |
| } |
| else if (expression instanceof PsiConditionalExpression) { |
| final ConditionalKind conditionalKind = isBooleanOrNumeric(expression); |
| if (conditionalKind == null) { |
| return isInAssignmentOrInvocationContext(expression); |
| } |
| } |
| return false; |
| } |
| |
| public static boolean isMethodCallPolyExpression(PsiExpression expression, final PsiMethod method) { |
| if (isInAssignmentOrInvocationContext(expression) && ((PsiCallExpression)expression).getTypeArguments().length == 0) { |
| if (method != null) { |
| final Set<PsiTypeParameter> typeParameters = new HashSet<PsiTypeParameter>(Arrays.asList(method.getTypeParameters())); |
| if (!typeParameters.isEmpty()) { |
| final PsiType returnType = method.getReturnType(); |
| if (returnType != null) { |
| return mentionsTypeParameters(returnType, typeParameters); |
| } |
| } |
| } else { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static Boolean mentionsTypeParameters(@Nullable PsiType returnType, final Set<PsiTypeParameter> typeParameters) { |
| if (returnType == null) return false; |
| return returnType.accept(new PsiTypeVisitor<Boolean>() { |
| @Nullable |
| @Override |
| public Boolean visitType(PsiType type) { |
| return false; |
| } |
| |
| @Nullable |
| @Override |
| public Boolean visitWildcardType(PsiWildcardType wildcardType) { |
| final PsiType bound = wildcardType.getBound(); |
| if (bound != null) { |
| return bound.accept(this); |
| } |
| return false; |
| } |
| |
| @Nullable |
| @Override |
| public Boolean visitClassType(PsiClassType classType) { |
| for (PsiType type : classType.getParameters()) { |
| if (type.accept(this)) return true; |
| } |
| final PsiClass psiClass = classType.resolve(); |
| return psiClass instanceof PsiTypeParameter && typeParameters.contains(psiClass); |
| } |
| |
| @Nullable |
| @Override |
| public Boolean visitArrayType(PsiArrayType arrayType) { |
| return arrayType.getComponentType().accept(this); |
| } |
| }); |
| } |
| |
| private static boolean isInAssignmentOrInvocationContext(PsiExpression expr) { |
| final PsiElement context = expr.getParent(); |
| return context instanceof PsiExpressionList || context instanceof PsiConditionalExpression || isAssignmentContext(expr, context); |
| } |
| |
| private static boolean isAssignmentContext(PsiExpression expr, PsiElement context) { |
| return PsiUtil.isCondition(expr, context) || |
| context instanceof PsiReturnStatement || |
| context instanceof PsiAssignmentExpression || |
| context instanceof PsiVariable || |
| context instanceof PsiLambdaExpression; |
| } |
| |
| private enum ConditionalKind { |
| BOOLEAN, NUMERIC |
| } |
| |
| private static ConditionalKind isBooleanOrNumeric(PsiExpression expr) { |
| if (expr instanceof PsiParenthesizedExpression) { |
| return isBooleanOrNumeric(((PsiParenthesizedExpression)expr).getExpression()); |
| } |
| if (expr == null) return null; |
| PsiType type = null; |
| if (expr instanceof PsiNewExpression || hasStandaloneForm(expr)) { |
| type = expr.getType(); |
| } else if (expr instanceof PsiMethodCallExpression) { |
| final PsiMethod method = ((PsiMethodCallExpression)expr).resolveMethod(); |
| if (method != null) { |
| type = method.getReturnType(); |
| } |
| } |
| if (TypeConversionUtil.isNumericType(type)) return ConditionalKind.NUMERIC; |
| if (TypeConversionUtil.isBooleanType(type)) return ConditionalKind.BOOLEAN; |
| if (expr instanceof PsiConditionalExpression) { |
| final PsiExpression thenExpression = ((PsiConditionalExpression)expr).getThenExpression(); |
| final PsiExpression elseExpression = ((PsiConditionalExpression)expr).getElseExpression(); |
| final ConditionalKind thenKind = isBooleanOrNumeric(thenExpression); |
| final ConditionalKind elseKind = isBooleanOrNumeric(elseExpression); |
| if (thenKind == elseKind || elseKind == null) return thenKind; |
| if (thenKind == null) return elseKind; |
| } |
| return null; |
| } |
| } |