/*
* 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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.ClassCandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* User: anna
* Date: 7/17/12
*/
public class LambdaUtil {
private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
@NonNls public static final String JAVA_LANG_FUNCTIONAL_INTERFACE = "java.lang.FunctionalInterface";
@Nullable
public static PsiType getFunctionalInterfaceReturnType(PsiLambdaExpression expr) {
return getFunctionalInterfaceReturnType(expr.getFunctionalInterfaceType());
}
@Nullable
public static PsiType getFunctionalInterfaceReturnType(@Nullable PsiType functionalInterfaceType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature != null) {
final PsiType returnType = getReturnType(psiClass, methodSignature);
return resolveResult.getSubstitutor().substitute(returnType);
}
}
return null;
}
@Nullable
public static PsiMethod getFunctionalInterfaceMethod(@Nullable PsiType functionalInterfaceType) {
return getFunctionalInterfaceMethod(PsiUtil.resolveGenericsClassInType(functionalInterfaceType));
}
@Nullable
public static PsiMethod getFunctionalInterfaceMethod(PsiClassType.ClassResolveResult result) {
final PsiClass psiClass = result.getElement();
if (psiClass != null) {
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature != null) {
return getMethod(psiClass, methodSignature);
}
}
return null;
}
public static PsiSubstitutor getSubstitutor(@NotNull PsiMethod method, @NotNull PsiClassType.ClassResolveResult resolveResult) {
final PsiClass derivedClass = resolveResult.getElement();
LOG.assertTrue(derivedClass != null);
final PsiClass methodContainingClass = method.getContainingClass();
LOG.assertTrue(methodContainingClass != null);
PsiSubstitutor initialSubst = resolveResult.getSubstitutor();
final PsiSubstitutor superClassSubstitutor =
TypeConversionUtil.getSuperClassSubstitutor(methodContainingClass, derivedClass, PsiSubstitutor.EMPTY);
for (PsiTypeParameter param : superClassSubstitutor.getSubstitutionMap().keySet()) {
final PsiType substitute = superClassSubstitutor.substitute(param);
if (substitute != null) {
initialSubst = initialSubst.put(param, initialSubst.substitute(substitute));
}
}
return initialSubst;
}
public static boolean isValidLambdaContext(@Nullable PsiElement context) {
return context instanceof PsiTypeCastExpression ||
context instanceof PsiAssignmentExpression ||
context instanceof PsiVariable ||
context instanceof PsiLambdaExpression ||
context instanceof PsiReturnStatement ||
context instanceof PsiExpressionList ||
context instanceof PsiParenthesizedExpression ||
context instanceof PsiArrayInitializerExpression ||
context instanceof PsiConditionalExpression;
}
public static boolean isLambdaFullyInferred(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
final boolean hasParams = expression.getParameterList().getParametersCount() > 0;
if (hasParams || getFunctionalInterfaceReturnType(functionalInterfaceType) != PsiType.VOID) { //todo check that void lambdas without params check
return !dependsOnTypeParams(functionalInterfaceType, functionalInterfaceType, expression);
}
return true;
}
private static boolean checkRawAcceptable(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
PsiElement parent = expression.getParent();
while (parent instanceof PsiParenthesizedExpression) {
parent = parent.getParent();
}
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiMethodCallExpression) {
final PsiExpression qualifierExpression = ((PsiMethodCallExpression)gParent).getMethodExpression().getQualifierExpression();
final PsiType type = qualifierExpression != null ? qualifierExpression.getType() : null;
if (type instanceof PsiClassType && ((PsiClassType)type).isRaw()) {
return true;
}
final PsiMethod method = ((PsiMethodCallExpression)gParent).resolveMethod();
if (method != null) {
int lambdaIdx = getLambdaIdx((PsiExpressionList)parent, expression);
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiType normalizedType = getNormalizedType(parameters[adjustLambdaIdx(lambdaIdx, method, parameters)]);
if (normalizedType instanceof PsiClassType && ((PsiClassType)normalizedType).isRaw()) return true;
}
}
if (functionalInterfaceType instanceof PsiClassType && ((PsiClassType)functionalInterfaceType).isRaw()){
return false;
}
}
return true;
}
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType, boolean checkReturnType) {
if (leftType instanceof PsiIntersectionType) {
for (PsiType conjunctType : ((PsiIntersectionType)leftType).getConjuncts()) {
if (isAcceptable(lambdaExpression, conjunctType, checkReturnType)) return true;
}
return false;
}
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(leftType));
final PsiClass psiClass = resolveResult.getElement();
if (psiClass instanceof PsiAnonymousClass) {
return isAcceptable(lambdaExpression, ((PsiAnonymousClass)psiClass).getBaseClassType(), checkReturnType);
}
final MethodSignature methodSignature = getFunction(psiClass);
if (methodSignature == null) return false;
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
final PsiType[] parameterTypes = methodSignature.getParameterTypes();
if (lambdaParameters.length != parameterTypes.length) return false;
for (int lambdaParamIdx = 0, length = lambdaParameters.length; lambdaParamIdx < length; lambdaParamIdx++) {
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
final PsiTypeElement typeElement = parameter.getTypeElement();
if (typeElement != null) {
final PsiType lambdaFormalType = typeElement.getType();
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
if (lambdaFormalType instanceof PsiPrimitiveType) {
if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.equals(lambdaFormalType);
return false;
}
if (!TypeConversionUtil.erasure(lambdaFormalType)
.isAssignableFrom(TypeConversionUtil.erasure(GenericsUtil.eliminateWildcards(
resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType)))))) {
return false;
}
}
}
if (checkReturnType) {
final String uniqueVarName =
JavaCodeStyleManager.getInstance(lambdaExpression.getProject()).suggestUniqueVariableName("l", lambdaExpression, true);
String canonicalText = leftType.getCanonicalText();
if (leftType instanceof PsiEllipsisType) {
canonicalText = ((PsiEllipsisType)leftType).toArrayType().getCanonicalText();
}
final PsiStatement assignmentFromText = JavaPsiFacade.getElementFactory(lambdaExpression.getProject())
.createStatementFromText(canonicalText + " " + uniqueVarName + " = " + lambdaExpression.getText(), lambdaExpression);
final PsiLocalVariable localVariable = (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
LOG.assertTrue(psiClass != null);
PsiType methodReturnType = getReturnType(psiClass, methodSignature);
if (methodReturnType != null) {
methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType));
return LambdaHighlightingUtil.checkReturnTypeCompatible((PsiLambdaExpression)localVariable.getInitializer(), methodReturnType) == null;
}
}
return true;
}
@Nullable
static MethodSignature getFunction(PsiClass psiClass) {
if (psiClass == null) return null;
final List<MethodSignature> functions = findFunctionCandidates(psiClass);
if (functions != null && functions.size() == 1) {
return functions.get(0);
}
return null;
}
private static boolean overridesPublicObjectMethod(PsiMethod psiMethod) {
boolean overrideObject = false;
for (PsiMethod superMethod : psiMethod.findDeepestSuperMethods()) {
final PsiClass containingClass = superMethod.getContainingClass();
if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
if (superMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
overrideObject = true;
break;
}
}
}
return overrideObject;
}
private static MethodSignature getMethodSignature(PsiMethod method, PsiClass psiClass, PsiClass containingClass) {
final MethodSignature methodSignature;
if (containingClass != null && containingClass != psiClass) {
methodSignature = method.getSignature(TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY));
}
else {
methodSignature = method.getSignature(PsiSubstitutor.EMPTY);
}
return methodSignature;
}
@Nullable
private static List<MethodSignature> hasSubsignature(List<MethodSignature> signatures) {
for (MethodSignature signature : signatures) {
boolean subsignature = true;
for (MethodSignature methodSignature : signatures) {
if (!signature.equals(methodSignature)) {
if (!MethodSignatureUtil.isSubsignature(signature, methodSignature)) {
subsignature = false;
break;
}
}
}
if (subsignature) return Collections.singletonList(signature);
}
return signatures;
}
@Nullable
static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) {
if (psiClass instanceof PsiAnonymousClass) {
psiClass = PsiUtil.resolveClassInType(((PsiAnonymousClass)psiClass).getBaseClassType());
}
if (psiClass != null && psiClass.isInterface()) {
final List<MethodSignature> methods = new ArrayList<MethodSignature>();
final Collection<HierarchicalMethodSignature> visibleSignatures = psiClass.getVisibleSignatures();
for (HierarchicalMethodSignature signature : visibleSignatures) {
final PsiMethod psiMethod = signature.getMethod();
if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) continue;
if (!overridesPublicObjectMethod(psiMethod)) {
methods.add(signature);
}
}
return hasSubsignature(methods);
}
return null;
}
@Nullable
private static PsiType getReturnType(PsiClass psiClass, MethodSignature methodSignature) {
final PsiMethod method = getMethod(psiClass, methodSignature);
if (method != null) {
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return null;
return TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY).substitute(method.getReturnType());
}
else {
return null;
}
}
@Nullable
private static PsiMethod getMethod(PsiClass psiClass, MethodSignature methodSignature) {
final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
for (PsiMethod psiMethod : methodsByName) {
if (MethodSignatureUtil
.areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
return psiMethod;
}
}
return null;
}
public static int getLambdaIdx(PsiExpressionList expressionList, final PsiElement element) {
PsiExpression[] expressions = expressionList.getExpressions();
for (int i = 0; i < expressions.length; i++) {
PsiExpression expression = expressions[i];
if (PsiTreeUtil.isAncestor(expression, element, false)) {
return i;
}
}
return -1;
}
public static boolean dependsOnTypeParams(PsiType type,
PsiLambdaExpression expr,
PsiTypeParameter param2Check) {
return depends(type, new TypeParamsChecker(expr), param2Check);
}
public static boolean dependsOnTypeParams(PsiType type,
PsiType functionalInterfaceType,
PsiElement lambdaExpression,
PsiTypeParameter... param2Check) {
return depends(type, new TypeParamsChecker(lambdaExpression,
PsiUtil.resolveClassInType(functionalInterfaceType)), param2Check);
}
public static boolean dependsOnTypeParams(PsiType type,
PsiClass aClass,
PsiMethod aMethod) {
return depends(type, new TypeParamsChecker(aMethod, aClass));
}
static boolean depends(PsiType type, TypeParamsChecker visitor, PsiTypeParameter... param2Check) {
if (!visitor.startedInference()) return false;
final Boolean accept = type.accept(visitor);
if (param2Check.length > 0) {
return visitor.used(param2Check);
}
return accept != null && accept.booleanValue();
}
public static boolean isFreeFromTypeInferenceArgs(final PsiParameter[] methodParameters,
final PsiLambdaExpression lambdaExpression,
final PsiExpression expression,
final PsiSubstitutor subst,
final PsiType functionalInterfaceType,
final PsiTypeParameter typeParam) {
if (expression instanceof PsiCallExpression && ((PsiCallExpression)expression).getTypeArguments().length > 0) return true;
if (expression instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)expression).getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiReferenceParameterList parameterList = classReference.getParameterList();
if (parameterList != null) {
final PsiTypeElement[] typeParameterElements = parameterList.getTypeParameterElements();
if (typeParameterElements.length > 0) {
if (!(typeParameterElements[0].getType() instanceof PsiDiamondType)) {
return true;
}
}
}
}
}
final PsiParameter[] lambdaParams = lambdaExpression.getParameterList().getParameters();
if (lambdaParams.length != methodParameters.length) return false;
final boolean[] independent = new boolean[]{true};
final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod == null) return false;
final TypeParamsChecker paramsChecker = new TypeParamsChecker(lambdaExpression);
for (PsiParameter parameter : interfaceMethod.getParameterList().getParameters()) {
subst.substitute(parameter.getType()).accept(paramsChecker);
}
paramsChecker.myUsedTypeParams.add(typeParam);
expression.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitConditionalExpression(PsiConditionalExpression expression) {
final PsiExpression thenExpression = expression.getThenExpression();
if (thenExpression != null) {
thenExpression.accept(this);
}
final PsiExpression elseExpression = expression.getElseExpression();
if (elseExpression != null) {
elseExpression.accept(this);
}
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
int usedParamIdx = -1;
for (int i = 0; i < lambdaParams.length; i++) {
PsiParameter param = lambdaParams[i];
if (expression.isReferenceTo(param)) {
usedParamIdx = i;
break;
}
}
if (usedParamIdx > -1 && dependsOnTypeParams(subst.substitute(methodParameters[usedParamIdx].getType()), functionalInterfaceType,
lambdaExpression, paramsChecker.myUsedTypeParams.toArray(new PsiTypeParameter[paramsChecker.myUsedTypeParams.size()]))) {
independent[0] = false;
}
}
});
return independent[0];
}
@Nullable
public static PsiType getFunctionalInterfaceType(PsiElement expression, final boolean tryToSubstitute) {
return getFunctionalInterfaceType(expression, tryToSubstitute, -1);
}
@Nullable
public static PsiType getFunctionalInterfaceType(PsiElement expression, final boolean tryToSubstitute, int paramIdx) {
PsiElement parent = expression.getParent();
PsiElement element = expression;
while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) {
if (parent instanceof PsiConditionalExpression &&
((PsiConditionalExpression)parent).getThenExpression() != element &&
((PsiConditionalExpression)parent).getElseExpression() != element) break;
element = parent;
parent = parent.getParent();
}
if (parent instanceof PsiArrayInitializerExpression) {
final PsiType psiType = ((PsiArrayInitializerExpression)parent).getType();
if (psiType instanceof PsiArrayType) {
return ((PsiArrayType)psiType).getComponentType();
}
} else if (parent instanceof PsiTypeCastExpression) {
final PsiType castType = ((PsiTypeCastExpression)parent).getType();
if (castType instanceof PsiIntersectionType) {
for (PsiType conjunctType : ((PsiIntersectionType)castType).getConjuncts()) {
if (getFunctionalInterfaceMethod(conjunctType) != null) return conjunctType;
}
}
return castType;
}
else if (parent instanceof PsiVariable) {
return ((PsiVariable)parent).getType();
}
else if (parent instanceof PsiAssignmentExpression && expression instanceof PsiExpression && !PsiUtil.isOnAssignmentLeftHand((PsiExpression)expression)) {
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
return lExpression.getType();
}
else if (parent instanceof PsiExpressionList) {
final PsiExpressionList expressionList = (PsiExpressionList)parent;
final int lambdaIdx = getLambdaIdx(expressionList, expression);
if (lambdaIdx > -1) {
PsiType cachedType = null;
final Pair<PsiMethod, PsiSubstitutor> method = MethodCandidateInfo.getCurrentMethod(parent);
if (method != null) {
final PsiParameter[] parameters = method.first.getParameterList().getParameters();
cachedType = lambdaIdx < parameters.length ? method.second.substitute(getNormalizedType(parameters[adjustLambdaIdx(lambdaIdx, method.first, parameters)])) : null;
if (!tryToSubstitute) return cachedType;
}
final PsiElement gParent = expressionList.getParent();
if (gParent instanceof PsiCall) {
final PsiCall contextCall = (PsiCall)gParent;
final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
final PsiElement resolve = resolveResult.getElement();
if (resolve instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters);
if (finalLambdaIdx < parameters.length) {
if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]);
if (cachedType != null && paramIdx > -1) {
final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(cachedType);
if (interfaceMethod != null) {
final PsiClassType.ClassResolveResult cachedResult = PsiUtil.resolveGenericsClassInType(cachedType);
final PsiType interfaceMethodParameterType = interfaceMethod.getParameterList().getParameters()[paramIdx].getType();
if (!dependsOnTypeParams(cachedResult.getSubstitutor().substitute(interfaceMethodParameterType), cachedType, expression)){
return cachedType;
}
}
}
return PsiResolveHelper.ourGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() {
@Override
public PsiType compute() {
return resolveResult.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx]));
}
});
}
}
return null;
}
}
}
else if (parent instanceof PsiReturnStatement) {
final PsiLambdaExpression gParent = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class);
if (gParent != null) {
return getFunctionalInterfaceTypeByContainingLambda(gParent);
} else {
final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
if (method != null) {
return method.getReturnType();
}
}
}
else if (parent instanceof PsiLambdaExpression) {
return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression)parent);
}
return null;
}
private static PsiType getFunctionalInterfaceTypeByContainingLambda(@NotNull PsiLambdaExpression parentLambda) {
final PsiType parentInterfaceType = parentLambda.getFunctionalInterfaceType();
return parentInterfaceType != null ? getFunctionalInterfaceReturnType(parentInterfaceType) : null;
}
private static int adjustLambdaIdx(int lambdaIdx, PsiMethod resolve, PsiParameter[] parameters) {
final int finalLambdaIdx;
if (resolve.isVarArgs() && lambdaIdx >= parameters.length) {
finalLambdaIdx = parameters.length - 1;
} else {
finalLambdaIdx = lambdaIdx;
}
return finalLambdaIdx;
}
private static PsiType getNormalizedType(PsiParameter parameter) {
final PsiType type = parameter.getType();
if (type instanceof PsiEllipsisType) {
return ((PsiEllipsisType)type).getComponentType();
}
return type;
}
public static PsiType getLambdaParameterType(PsiParameter param) {
final PsiElement paramParent = param.getParent();
if (paramParent instanceof PsiParameterList) {
final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param);
if (parameterIndex > -1) {
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class);
if (lambdaExpression != null) {
PsiType type = getFunctionalInterfaceType(lambdaExpression, true, parameterIndex);
if (type == null) {
type = getFunctionalInterfaceType(lambdaExpression, false);
}
if (type instanceof PsiIntersectionType) {
final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
for (PsiType conjunct : conjuncts) {
final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, lambdaExpression, conjunct);
if (lambdaParameterFromType != null) return lambdaParameterFromType;
}
} else {
final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, lambdaExpression, type);
if (lambdaParameterFromType != null) {
return lambdaParameterFromType;
}
}
}
}
}
return new PsiLambdaParameterType(param);
}
private static PsiType getLambdaParameterFromType(int parameterIndex, PsiLambdaExpression lambdaExpression, PsiType conjunct) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(conjunct);
if (resolveResult != null) {
final PsiMethod method = getFunctionalInterfaceMethod(conjunct);
if (method != null) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameterIndex < parameters.length) {
final PsiType psiType = getSubstitutor(method, resolveResult).substitute(parameters[parameterIndex].getType());
if (!dependsOnTypeParams(psiType, conjunct, lambdaExpression)) {
return GenericsUtil.eliminateWildcards(psiType);
}
}
}
}
return null;
}
public static PsiSubstitutor inferFromReturnType(final PsiTypeParameter[] typeParameters,
final PsiType returnType,
@Nullable final PsiType interfaceMethodReturnType,
PsiSubstitutor psiSubstitutor,
final LanguageLevel languageLevel,
final Project project) {
if (interfaceMethodReturnType == null) return psiSubstitutor;
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
for (PsiTypeParameter typeParameter : typeParameters) {
final PsiType constraint = resolveHelper.getSubstitutionForTypeParameter(typeParameter, returnType, interfaceMethodReturnType, false, languageLevel);
if (constraint != PsiType.NULL && constraint != null) {
PsiType inferredType = null;
final PsiClassType[] bounds = typeParameter.getExtendsListTypes();
for (PsiClassType classTypeBound : bounds) {
if (TypeConversionUtil.isAssignable(classTypeBound, constraint)) {
inferredType = constraint;
break;
}
}
if (bounds.length == 0) {
inferredType = constraint;
}
if (inferredType != null) {
psiSubstitutor = psiSubstitutor.put(typeParameter, inferredType);
}
}
}
return psiSubstitutor;
}
public static boolean notInferredType(PsiType typeByExpression) {
return typeByExpression instanceof PsiMethodReferenceType || typeByExpression instanceof PsiLambdaExpressionType || typeByExpression instanceof PsiLambdaParameterType;
}
public static List<PsiReturnStatement> getReturnStatements(PsiLambdaExpression lambdaExpression) {
final PsiElement body = lambdaExpression.getBody();
final List<PsiReturnStatement> result = new ArrayList<PsiReturnStatement>();
if (body != null) {
body.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitReturnStatement(PsiReturnStatement statement) {
result.add(statement);
}
@Override
public void visitClass(PsiClass aClass) {
}
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
}
});
}
return result;
}
public static List<PsiExpression> getReturnExpressions(PsiLambdaExpression lambdaExpression) {
final PsiElement body = lambdaExpression.getBody();
if (body instanceof PsiExpression) {
//if (((PsiExpression)body).getType() != PsiType.VOID) return Collections.emptyList();
return Collections.singletonList((PsiExpression)body);
}
final List<PsiExpression> result = new ArrayList<PsiExpression>();
for (PsiReturnStatement returnStatement : getReturnStatements(lambdaExpression)) {
final PsiExpression returnValue = returnStatement.getReturnValue();
if (returnValue != null) {
result.add(returnValue);
}
}
return result;
}
@Nullable
public static String checkFunctionalInterface(@NotNull PsiAnnotation annotation, @NotNull LanguageLevel languageLevel) {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && Comparing.strEqual(annotation.getQualifiedName(), JAVA_LANG_FUNCTIONAL_INTERFACE)) {
final PsiAnnotationOwner owner = annotation.getOwner();
if (owner instanceof PsiModifierList) {
final PsiElement parent = ((PsiModifierList)owner).getParent();
if (parent instanceof PsiClass) {
return LambdaHighlightingUtil.checkInterfaceFunctional((PsiClass)parent, ((PsiClass)parent).getName() + " is not a functional interface");
}
}
}
return null;
}
public static boolean isValidQualifier4InterfaceStaticMethodCall(@NotNull PsiMethod method,
@NotNull PsiReferenceExpression methodReferenceExpression,
@NotNull LanguageLevel languageLevel) {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiExpression qualifierExpression = methodReferenceExpression.getQualifierExpression();
final PsiClass containingClass = method.getContainingClass();
if (containingClass != null && containingClass.isInterface() && method.hasModifierProperty(PsiModifier.STATIC)) {
return qualifierExpression == null && PsiTreeUtil.isAncestor(containingClass, methodReferenceExpression, true)||
qualifierExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifierExpression).resolve() == containingClass;
}
}
return true;
}
static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
private final Set<PsiTypeParameter> myUsedTypeParams = new HashSet<PsiTypeParameter>();
private TypeParamsChecker(PsiMethod method, PsiClass aClass) {
myMethod = method;
myClass = aClass;
}
public TypeParamsChecker(PsiElement expression) {
this(expression, PsiUtil.resolveGenericsClassInType(getFunctionalInterfaceType(expression, false)).getElement());
}
public TypeParamsChecker(PsiElement expression, PsiClass aClass) {
myClass = aClass;
PsiElement parent = expression != null ? expression.getParent() : null;
while (parent instanceof PsiParenthesizedExpression) {
parent = parent.getParent();
}
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiCall) {
final Pair<PsiMethod, PsiSubstitutor> pair = MethodCandidateInfo.getCurrentMethod(parent);
myMethod = pair != null ? pair.first : null;
if (myMethod == null) {
myMethod = ((PsiCall)gParent).resolveMethod();
}
if (myMethod != null && PsiTreeUtil.isAncestor(myMethod, expression, false)) {
myMethod = null;
}
}
}
}
public boolean startedInference() {
return myMethod != null;
}
@Override
public Boolean visitClassType(PsiClassType classType) {
boolean used = false;
for (PsiType paramType : classType.getParameters()) {
final Boolean paramAccepted = paramType.accept(this);
used |= paramAccepted != null && paramAccepted.booleanValue();
}
final PsiClass resolve = classType.resolve();
if (resolve instanceof PsiTypeParameter) {
final PsiTypeParameter typeParameter = (PsiTypeParameter)resolve;
if (check(typeParameter)) {
myUsedTypeParams.add(typeParameter);
return true;
}
}
return used;
}
@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 visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
return visitWildcardType(capturedWildcardType.getWildcard());
}
@Nullable
@Override
public Boolean visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
return true;
}
@Nullable
@Override
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
@Override
public Boolean visitType(PsiType type) {
return false;
}
private boolean check(PsiTypeParameter check) {
final PsiTypeParameterListOwner owner = check.getOwner();
if (owner == myMethod) {
return true;
}
else if (owner == myClass) {
return true;
}
return false;
}
public boolean used(PsiTypeParameter... parameters) {
for (PsiTypeParameter parameter : parameters) {
if (myUsedTypeParams.contains(parameter)) return true;
}
return false;
}
}
}