mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda: infer from return type; cyclic inference stop
This commit is contained in:
+17
@@ -239,6 +239,23 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {
|
||||
myHolder.add(HighlightUtil.checkLambdaFeature(expression));
|
||||
if (!myHolder.hasErrorResults()) {
|
||||
if (LambdaUtil.isValidLambdaContext(expression.getParent())) {
|
||||
final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
|
||||
if (functionalInterfaceType != null) {
|
||||
final String notFunctionalMessage = LambdaUtil.checkInterfaceFunctional(functionalInterfaceType);
|
||||
if (notFunctionalMessage != null) {
|
||||
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, notFunctionalMessage));
|
||||
} else {
|
||||
if (!LambdaUtil.isLambdaFullyInferred(expression, functionalInterfaceType)) {
|
||||
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, "Cyclic inference")); //todo[ann] append not inferred type params info
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, "Lambda expression not expected here"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,86 +32,71 @@ import java.util.List;
|
||||
public class LambdaUtil {
|
||||
private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
|
||||
|
||||
@Nullable
|
||||
public static PsiType getFunctionalInterfaceReturnType(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(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) {
|
||||
return getMethod(psiClass, methodSignature);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isValidLambdaContext(PsiElement context) {
|
||||
return context instanceof PsiTypeCastExpression ||
|
||||
context instanceof PsiAssignmentExpression ||
|
||||
context instanceof PsiVariable ||
|
||||
context instanceof PsiLambdaExpression ||
|
||||
context instanceof PsiReturnStatement ||
|
||||
context instanceof PsiExpressionList ||
|
||||
context instanceof PsiParenthesizedExpression;
|
||||
}
|
||||
|
||||
public static boolean isLambdaFullyInferred(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
|
||||
if (expression.getParameterList().getParametersCount() > 0 || getFunctionalInterfaceReturnType(functionalInterfaceType) != PsiType.VOID) { //todo check that void lambdas without params check
|
||||
final Boolean accept = functionalInterfaceType.accept(new TypeParamsChecker(expression));
|
||||
return accept == null || !accept.booleanValue();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String checkInterfaceFunctional(PsiType functionalInterfaceType) {
|
||||
final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(functionalInterfaceType);
|
||||
if (aClass != null) {
|
||||
return checkInterfaceFunctional(aClass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) {
|
||||
if (psiClass instanceof PsiTypeParameter) return null; //should be logged as cyclic inference
|
||||
final List<MethodSignature> signatures = findFunctionCandidates(psiClass);
|
||||
if (signatures == null) return "Target type of a lambda conversion must be an interface";
|
||||
if (signatures.isEmpty()) return "No target method found";
|
||||
return signatures.size() == 1 ? null : "Multiple non-overriding abstract methods found";
|
||||
}
|
||||
|
||||
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
|
||||
private static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) {
|
||||
if (psiClass.isInterface()) {
|
||||
final List<MethodSignature> methods = new ArrayList<MethodSignature>();
|
||||
final PsiMethod[] psiClassMethods = psiClass.getAllMethods();
|
||||
for (PsiMethod psiMethod : psiClassMethods) {
|
||||
if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
|
||||
final PsiClass methodContainingClass = psiMethod.getContainingClass();
|
||||
if (!overridesPublicObjectMethod(psiMethod)) {
|
||||
methods.add(getMethodSignature(psiMethod, psiClass, methodContainingClass));
|
||||
}
|
||||
}
|
||||
|
||||
return hasSubsignature(methods);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public 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;
|
||||
}
|
||||
|
||||
public static PsiType getLambdaParameterType(PsiParameter param) {
|
||||
|
||||
final PsiElement paramParent = param.getParent();
|
||||
if (paramParent instanceof PsiParameterList) {
|
||||
final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param);
|
||||
@@ -130,7 +115,7 @@ public class LambdaUtil {
|
||||
if (bound != null) {
|
||||
return bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
return psiType;
|
||||
}
|
||||
}
|
||||
@@ -197,27 +182,19 @@ public class LambdaUtil {
|
||||
if (typeElement != null) {
|
||||
final PsiType lambdaFormalType = typeElement.getType();
|
||||
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
|
||||
if (lambdaFormalType instanceof PsiPrimitiveType){
|
||||
if (lambdaFormalType instanceof PsiPrimitiveType) {
|
||||
if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.isAssignableFrom(lambdaFormalType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType)).isAssignableFrom(lambdaFormalType)) {
|
||||
if (!resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType))
|
||||
.isAssignableFrom(lambdaFormalType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiMethod method = null;
|
||||
LOG.assertTrue(psiClass != null);
|
||||
final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
|
||||
for (PsiMethod psiMethod : methodsByName) {
|
||||
if (MethodSignatureUtil.areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
|
||||
method = psiMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG.assertTrue(method != null);
|
||||
PsiType methodReturnType = method.getReturnType();
|
||||
PsiType methodReturnType = getReturnType(psiClass, methodSignature);
|
||||
if (methodReturnType != null && methodReturnType != PsiType.VOID) {
|
||||
methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType));
|
||||
final PsiElement body = lambdaExpression.getBody();
|
||||
@@ -231,14 +208,108 @@ public class LambdaUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (body instanceof PsiExpression) {
|
||||
}
|
||||
else if (body instanceof PsiExpression) {
|
||||
return checkReturnTypeAssignability(((PsiExpression)body).getType(), parameterTypes, lambdaExpression, methodReturnType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkReturnTypeAssignability(PsiType lambdaReturnType, PsiType[] parameterTypes, PsiLambdaExpression lambdaExpression, PsiType methodReturnType) {
|
||||
@Nullable
|
||||
private 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
|
||||
private static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) {
|
||||
if (psiClass.isInterface()) {
|
||||
final List<MethodSignature> methods = new ArrayList<MethodSignature>();
|
||||
final PsiMethod[] psiClassMethods = psiClass.getAllMethods();
|
||||
for (PsiMethod psiMethod : psiClassMethods) {
|
||||
if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) continue;
|
||||
final PsiClass methodContainingClass = psiMethod.getContainingClass();
|
||||
if (!overridesPublicObjectMethod(psiMethod)) {
|
||||
methods.add(getMethodSignature(psiMethod, psiClass, methodContainingClass));
|
||||
}
|
||||
}
|
||||
|
||||
return hasSubsignature(methods);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static PsiType getReturnType(PsiClass psiClass, MethodSignature methodSignature) {
|
||||
final PsiMethod method = getMethod(psiClass, methodSignature);
|
||||
return method != null ? method.getReturnType() : 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;
|
||||
}
|
||||
|
||||
private static boolean checkReturnTypeAssignability(PsiType lambdaReturnType,
|
||||
PsiType[] parameterTypes,
|
||||
PsiLambdaExpression lambdaExpression,
|
||||
PsiType methodReturnType) {
|
||||
if (lambdaReturnType instanceof PsiLambdaParameterType) {
|
||||
final PsiParameter parameter = ((PsiLambdaParameterType)lambdaReturnType).getParameter();
|
||||
final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
|
||||
@@ -248,4 +319,58 @@ public class LambdaUtil {
|
||||
}
|
||||
return lambdaReturnType != null && methodReturnType.isAssignableFrom(lambdaReturnType);
|
||||
}
|
||||
|
||||
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
|
||||
private final PsiLambdaExpression myExpression;
|
||||
|
||||
public TypeParamsChecker(PsiLambdaExpression expression) {
|
||||
myExpression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitClassType(PsiClassType classType) {
|
||||
for (PsiType paramType : classType.getParameters()) {
|
||||
final Boolean paramAccepted = paramType.accept(this);
|
||||
if (paramAccepted != null && paramAccepted.booleanValue()) return true;
|
||||
}
|
||||
final PsiClass resolve = classType.resolve();
|
||||
if (resolve instanceof PsiTypeParameter) {
|
||||
if (!PsiTreeUtil.isAncestor(((PsiTypeParameter)resolve).getOwner(), myExpression, false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.intellij.psi;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a Java lambda expression.
|
||||
*/
|
||||
@@ -38,4 +40,9 @@ public interface PsiLambdaExpression extends PsiExpression {
|
||||
*/
|
||||
@Nullable
|
||||
PsiElement getBody();
|
||||
|
||||
List<PsiExpression> getReturnExpressions();
|
||||
|
||||
@Nullable
|
||||
PsiType getFunctionalInterfaceType();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public class PsiTypeVisitor<A> {
|
||||
|
||||
@Nullable
|
||||
public A visitLambdaExpressionType(PsiLambdaExpressionType lambdaExpressionType) {
|
||||
return visitType(LambdaUtil.getFunctionInterfaceType(lambdaExpressionType.getExpression()));
|
||||
final PsiLambdaExpression lambdaExpression = lambdaExpressionType.getExpression();
|
||||
return visitType(lambdaExpression.getFunctionalInterfaceType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.psi.infos;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
|
||||
@@ -27,6 +28,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @author ik, dsl
|
||||
*/
|
||||
public class MethodCandidateInfo extends CandidateInfo{
|
||||
public static final Key<PsiMethod> CURRENT_CANDIDATE = Key.create("CURRENT_CANDIDATE");
|
||||
@ApplicabilityLevelConstant private int myApplicabilityLevel = 0;
|
||||
private final PsiElement myArgumentList;
|
||||
private final PsiType[] myArgumentTypes;
|
||||
@@ -90,7 +92,13 @@ public class MethodCandidateInfo extends CandidateInfo{
|
||||
PsiSubstitutor incompleteSubstitutor = super.getSubstitutor();
|
||||
PsiMethod method = getElement();
|
||||
if (myTypeArguments == null) {
|
||||
myCalcedSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE);
|
||||
myArgumentList.putUserData(CURRENT_CANDIDATE, getElement());
|
||||
try {
|
||||
myCalcedSubstitutor = inferTypeArguments(DefaultParameterTypeInferencePolicy.INSTANCE);
|
||||
}
|
||||
finally {
|
||||
myArgumentList.putUserData(CURRENT_CANDIDATE, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiTypeParameter[] typeParams = method.getTypeParameters();
|
||||
|
||||
+149
-14
@@ -15,12 +15,15 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.source.resolve;
|
||||
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
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.tree.java.PsiLambdaExpressionImpl;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
|
||||
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
|
||||
import com.intellij.psi.scope.processor.MethodResolverProcessor;
|
||||
@@ -34,6 +37,8 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
static final RecursionGuard ourGuard = RecursionManager.createGuard("typeArgInference");
|
||||
private final PsiManager myManager;
|
||||
@@ -143,33 +148,59 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
|
||||
private static Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(final PsiTypeParameter typeParameter,
|
||||
final PsiParameter[] parameters,
|
||||
PsiExpression[] arguments,
|
||||
final PsiExpression[] arguments,
|
||||
final PsiSubstitutor partialSubstitutor,
|
||||
final PsiElement parent,
|
||||
final ParameterTypeInferencePolicy policy) {
|
||||
PsiType[] paramTypes = new PsiType[arguments.length];
|
||||
PsiType[] argTypes = new PsiType[arguments.length];
|
||||
if (parameters.length > 0) {
|
||||
for (int j = 0; j < argTypes.length; j++) {
|
||||
PsiExpression argument = arguments[j];
|
||||
if (argument == null) continue;
|
||||
if (argument instanceof PsiMethodCallExpression && ourGuard.currentStack().contains(argument)) continue;
|
||||
argTypes[j] = argument.getType();
|
||||
|
||||
final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)];
|
||||
if (j >= parameters.length && !parameter.isVarArgs()) break;
|
||||
paramTypes[j] = parameter.getType();
|
||||
if (paramTypes[j] instanceof PsiEllipsisType) {
|
||||
paramTypes[j] = ((PsiEllipsisType)paramTypes[j]).getComponentType();
|
||||
if (arguments.length == parameters.length && argTypes[j] instanceof PsiArrayType && !(((PsiArrayType)argTypes[j]).getComponentType() instanceof PsiPrimitiveType)) {
|
||||
argTypes[j] = ((PsiArrayType)argTypes[j]).getComponentType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inferTypeForMethodTypeParameterInner(typeParameter, paramTypes, argTypes, partialSubstitutor, parent, policy);
|
||||
}
|
||||
|
||||
private static Pair<PsiType, ConstraintType> inferTypeForMethodTypeParameterInner(final PsiTypeParameter typeParameter,
|
||||
final PsiType[] paramTypes,
|
||||
PsiType[] argTypes,
|
||||
PsiSubstitutor partialSubstitutor,
|
||||
PsiElement parent,
|
||||
@Nullable PsiElement parent,
|
||||
final ParameterTypeInferencePolicy policy) {
|
||||
PsiWildcardType wildcardToCapture = null;
|
||||
PsiType lowerBound = PsiType.NULL;
|
||||
PsiType upperBound = PsiType.NULL;
|
||||
if (parameters.length > 0) {
|
||||
for (int j = 0; j < arguments.length; j++) {
|
||||
PsiExpression argument = arguments[j];
|
||||
if (argument == null) continue;
|
||||
if (argument instanceof PsiMethodCallExpression && ourGuard.currentStack().contains(argument)) continue;
|
||||
|
||||
final PsiParameter parameter = parameters[Math.min(j, parameters.length - 1)];
|
||||
if (j >= parameters.length && !parameter.isVarArgs()) break;
|
||||
PsiType parameterType = parameter.getType();
|
||||
PsiType argumentType = argument.getType();
|
||||
if (paramTypes.length > 0) {
|
||||
for (int j = 0; j < argTypes.length; j++) {
|
||||
PsiType argumentType = argTypes[j];
|
||||
if (argumentType == null) continue;
|
||||
if (j >= paramTypes.length) break;
|
||||
|
||||
PsiType parameterType = paramTypes[j];
|
||||
if (parameterType == null) break;
|
||||
|
||||
if (parameterType instanceof PsiEllipsisType) {
|
||||
parameterType = ((PsiEllipsisType)parameterType).getComponentType();
|
||||
if (arguments.length == parameters.length && argumentType instanceof PsiArrayType && !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) {
|
||||
if (argTypes.length == paramTypes.length && argumentType instanceof PsiArrayType && !(((PsiArrayType)argumentType).getComponentType() instanceof PsiPrimitiveType)) {
|
||||
argumentType = ((PsiArrayType)argumentType).getComponentType();
|
||||
}
|
||||
}
|
||||
final Pair<PsiType,ConstraintType> currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType,
|
||||
argumentType, true, PsiUtil.getLanguageLevel(argument));
|
||||
argumentType, true, PsiUtil.getLanguageLevel(typeParameter));
|
||||
if (currentSubstitution == null) continue;
|
||||
if (currentSubstitution == FAILED_INFERENCE) {
|
||||
return getFailedInferenceConstraint(typeParameter);
|
||||
@@ -478,6 +509,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
if (paramClass == null) return null;
|
||||
|
||||
if (arg instanceof PsiLambdaExpressionType) {
|
||||
return inferSubstitutionFromLambda(typeParam, (PsiLambdaExpressionType)arg);
|
||||
}
|
||||
|
||||
if (!(arg instanceof PsiClassType)) return null;
|
||||
|
||||
JavaResolveResult argResult = ((PsiClassType)arg).resolveGenerics();
|
||||
@@ -500,6 +535,94 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
return getSubstitutionForTypeParameterInner(param, arg, patternType, ConstraintType.SUPERTYPE, 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<PsiType, ConstraintType> inferSubstitutionFromLambda(PsiTypeParameter typeParam, PsiLambdaExpressionType arg) {
|
||||
final PsiLambdaExpression lambdaExpression = arg.getExpression();
|
||||
if (PsiUtil.getLanguageLevel(lambdaExpression).isAtLeast(LanguageLevel.JDK_1_8)) {
|
||||
final PsiElement parent = skipParenthesizedExprUp(lambdaExpression.getParent());
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
||||
final PsiMethod method = expressionList.getUserData(MethodCandidateInfo.CURRENT_CANDIDATE);
|
||||
if (method != null) {
|
||||
final int i = ((PsiLambdaExpressionImpl)lambdaExpression).getLambdaIdx(expressionList);
|
||||
if (i < 0) return null;
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length <= i) return null;
|
||||
return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, parameters[i].getType());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return inferConstraintFromFunctionalInterfaceMethod(typeParam, lambdaExpression, lambdaExpression.getFunctionalInterfaceType());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
|
||||
PsiLambdaExpression lambdaExpression,
|
||||
PsiType functionalInterfaceType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
|
||||
if (method != null) {
|
||||
final Pair<PsiType, ConstraintType> constraintFromFormalParams = inferConstraintFromLambdaFormalParams(typeParam, resolveResult, method, lambdaExpression);
|
||||
if (constraintFromFormalParams != null) return constraintFromFormalParams;
|
||||
|
||||
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
|
||||
final PsiSubstitutor subst = resolveResult.getSubstitutor();
|
||||
final PsiType returnType = subst.substitute(method.getReturnType());
|
||||
if (returnType != null && returnType != PsiType.VOID) {
|
||||
final List<PsiExpression> expressions = lambdaExpression.getReturnExpressions();
|
||||
for (final PsiExpression expression : expressions) {
|
||||
PsiType exprType = ourGuard.doPreventingRecursion(lambdaExpression, true, new Computable<PsiType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
return expression.getType();
|
||||
}
|
||||
});
|
||||
if (exprType instanceof PsiLambdaParameterType) {
|
||||
final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter();
|
||||
final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
|
||||
if (parameterIndex > -1) {
|
||||
exprType = subst.substitute(methodParameters[parameterIndex].getType());
|
||||
}
|
||||
} else if (exprType instanceof PsiLambdaExpressionType) {
|
||||
return inferConstraintFromFunctionalInterfaceMethod(typeParam, ((PsiLambdaExpressionType)exprType).getExpression(), returnType);
|
||||
}
|
||||
Pair<PsiType, ConstraintType> constraint =
|
||||
getSubstitutionForTypeParameterConstraint(typeParam, returnType, exprType, false, PsiUtil.getLanguageLevel(method));
|
||||
if (constraint != null) {
|
||||
return constraint; //todo check that all return statements lead to the same inference
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<PsiType, ConstraintType> inferConstraintFromLambdaFormalParams(PsiTypeParameter typeParam,
|
||||
PsiClassType.ClassResolveResult resolveResult,
|
||||
PsiMethod method, PsiLambdaExpression lambdaExpression) {
|
||||
final PsiParameter[] parameters = lambdaExpression.getParameterList().getParameters();
|
||||
final PsiType[] lambdaArgs = new PsiType[parameters.length];
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
if (parameter.getTypeElement() == null) {
|
||||
return null;
|
||||
}
|
||||
lambdaArgs[i] = parameter.getType();
|
||||
}
|
||||
|
||||
final PsiSubstitutor subst = resolveResult.getSubstitutor();
|
||||
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
|
||||
PsiType[] methodParamTypes = new PsiType[methodParameters.length];
|
||||
for (int i = 0; i < methodParameters.length; i++) {
|
||||
methodParamTypes[i] = subst.substitute(methodParameters[i].getType());
|
||||
}
|
||||
return inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, lambdaArgs, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
|
||||
}
|
||||
|
||||
private static PsiType intersectAllExtends(PsiTypeParameter typeParam, PsiType arg) {
|
||||
if (arg == null) return null;
|
||||
PsiClassType[] superTypes = typeParam.getSuperTypes();
|
||||
@@ -666,6 +789,8 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
constraint = policy.inferTypeConstraintFromCallContext(methodCall, (PsiExpressionList)parent, (PsiCallExpression)pParent,
|
||||
typeParameter);
|
||||
}
|
||||
} else if (parent instanceof PsiLambdaExpression) {
|
||||
expectedType = LambdaUtil.getFunctionalInterfaceReturnType(((PsiLambdaExpression)parent).getFunctionalInterfaceType());
|
||||
}
|
||||
|
||||
final PsiManager manager = typeParameter.getManager();
|
||||
@@ -698,6 +823,16 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
|
||||
final Pair<PsiType, ConstraintType> result;
|
||||
if (constraint == null) {
|
||||
|
||||
final PsiExpressionList argumentList = methodCall.getArgumentList();
|
||||
if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) {
|
||||
for (PsiExpression expression : argumentList.getExpressions()) {
|
||||
if (expression instanceof PsiLambdaExpression){
|
||||
return getFailedInferenceConstraint(typeParameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final PsiSubstitutor finalSubstitutor = substitutor.put(typeParameter, null);
|
||||
PsiClassType[] superTypes = typeParameter.getSuperTypes();
|
||||
if (superTypes.length == 0) return null;
|
||||
|
||||
+104
@@ -15,14 +15,22 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.source.tree.java;
|
||||
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements PsiLambdaExpression {
|
||||
public static RecursionGuard ourGuard = RecursionManager.createGuard("Lambda");
|
||||
|
||||
public PsiLambdaExpressionImpl() {
|
||||
super(JavaElementType.LAMBDA_EXPRESSION);
|
||||
}
|
||||
@@ -39,6 +47,102 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
return element instanceof PsiExpression || element instanceof PsiCodeBlock ? element : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PsiExpression> getReturnExpressions() {
|
||||
final PsiElement body = 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>();
|
||||
if (body != null) {
|
||||
body.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitReturnStatement(PsiReturnStatement statement) {
|
||||
final PsiExpression returnValue = statement.getReturnValue();
|
||||
if (returnValue != null) {
|
||||
result.add(returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiType getFunctionalInterfaceType() {
|
||||
PsiElement parent = getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
PsiType type = null;
|
||||
if (parent instanceof PsiTypeCastExpression) {
|
||||
type = ((PsiTypeCastExpression)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiVariable) {
|
||||
type = ((PsiVariable)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
|
||||
type = lExpression.getType();
|
||||
}
|
||||
else if (parent instanceof PsiExpressionList) {
|
||||
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
||||
int lambdaIdx = getLambdaIdx(expressionList);
|
||||
|
||||
if (lambdaIdx > -1) {
|
||||
final PsiElement gParent = expressionList.getParent();
|
||||
if (gParent instanceof PsiMethodCallExpression) {
|
||||
final PsiMethodCallExpression contextCall = (PsiMethodCallExpression)gParent;
|
||||
final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
|
||||
final PsiElement resolve = resolveResult.getElement();
|
||||
if (resolve instanceof PsiMethod) {
|
||||
final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
|
||||
if (lambdaIdx < parameters.length) {
|
||||
type = parameters[lambdaIdx].getType();
|
||||
final PsiType psiType = type;
|
||||
type = ourGuard.doPreventingRecursion(this, true, new Computable<PsiType>() {
|
||||
@Override
|
||||
public PsiType compute() {
|
||||
return resolveResult.getSubstitutor().substitute(psiType);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiReturnStatement) {
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
|
||||
if (method != null) {
|
||||
type = method.getReturnType();
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiLambdaExpression) {
|
||||
final PsiType parentInterfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType();
|
||||
if (parentInterfaceType != null) {
|
||||
type = LambdaUtil.getFunctionalInterfaceReturnType(parentInterfaceType);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getLambdaIdx(PsiExpressionList expressionList) {
|
||||
PsiExpression[] expressions = expressionList.getExpressions();
|
||||
for (int i = 0; i < expressions.length; i++) {
|
||||
PsiExpression expression = expressions[i];
|
||||
if (PsiTreeUtil.isAncestor(expression, this, false)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType getType() {
|
||||
return new PsiLambdaExpressionType(this);
|
||||
|
||||
+1
-1
@@ -12,6 +12,6 @@ class Ambiguity1 {
|
||||
static <T> void m(I2<T> i2) {}
|
||||
|
||||
{
|
||||
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<Object>)' match">(()->{throw new AssertionError();})</error>;
|
||||
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<T>)' match">(()->{throw new AssertionError();})</error>;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -11,19 +11,21 @@ interface SameArgsI<T> {
|
||||
|
||||
class InferenceFromArgs {
|
||||
|
||||
private static <E> void bar(A<? extends E> a, I<? super E, Integer> i) { }
|
||||
private static <E> void bar(A<E> a, I<E, Integer> i) { }
|
||||
private static <E> void bazz(I<? super E, Integer> i) { }
|
||||
|
||||
void foo(B<Integer> b) {
|
||||
bar(b, (k, v) -> {int i = k; int j = v; return Math.max(i, j);});
|
||||
bar(null, <error descr="Cyclic inference">(k, v) -> v</error>);
|
||||
bar(null, null);
|
||||
bar(b, (k, v) -> {return v;});
|
||||
bar(b, (k, v) -> {<error descr="Incompatible types. Found: 'java.lang.Integer', required: 'java.lang.String'">String i = k;</error> return v;});
|
||||
bar(b, (k, v) -> {Integer i = k; return v;});
|
||||
|
||||
bazz(<error descr="Cyclic inference">(k, v) -> v</error>);
|
||||
bazz((k, v) -> {<error descr="Incompatible types. Found: 'E', required: 'int'">int i = k;</error> return v;});
|
||||
}
|
||||
|
||||
public static <T> SameArgsI<T> max() {
|
||||
return (a, b) -> b;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import java.util.*;
|
||||
interface In<X> {
|
||||
List<X> f();
|
||||
}
|
||||
|
||||
class InferenceOnMethodCallSite {
|
||||
<Z> void m(In<Z> i, In<Z> ii) { }
|
||||
<Z> void m(In<Z> s) { }
|
||||
|
||||
{
|
||||
m(<error descr="Cyclic inference">( ) -> Collections.emptyList()</error>);
|
||||
m((In<String>)() -> Collections.emptyList(), () -> new ArrayList<String>());
|
||||
m(() ->Collections.<String>emptyList(), () -> new ArrayList<String>());
|
||||
m(() -> Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -6,8 +6,8 @@ class Test {
|
||||
void foo(Object x) {}
|
||||
|
||||
void bar() {
|
||||
foo(<error descr="Operator '!' cannot be applied to '<lambda expression>'">!(int x)-> {}</error>);
|
||||
foo(<error descr="Lambda expression is not expected here">(int x)-> { } instanceof Object</error> );
|
||||
foo(!<error descr="Lambda expression not expected here">(int x)-> {}</error>);
|
||||
foo(<error descr="Lambda expression not expected here">(int x)-> { }</error> instanceof Object );
|
||||
}
|
||||
|
||||
I bazz() {
|
||||
|
||||
+2
-2
@@ -7,11 +7,11 @@ class NoInferenceResult {
|
||||
<T> void m1(T t) { }
|
||||
|
||||
void test() {
|
||||
m<error descr="'m(I<java.lang.Object,java.lang.Object>)' in 'NoInferenceResult' cannot be applied to '(<lambda expression>)'">((String s1) -> (String s2) -> s1 + s2)</error>;
|
||||
m((String s1) -> <error descr="Cyclic inference">(String s2) -> s1 + s2</error>);
|
||||
|
||||
m((String s1) -> s1.length());
|
||||
m((String s1) -> s1);
|
||||
|
||||
m1<error descr="'m1(java.lang.Object)' in 'NoInferenceResult' cannot be applied to '(<lambda expression>)'">(() -> { })</error>;
|
||||
m1(<error descr="Cyclic inference">() -> { }</error>);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ class ReturnTypeCompatibility {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
<error descr="Cannot resolve method 'call(<lambda expression>)'">call</error>(i-> {return i;});
|
||||
call(<error descr="Cyclic inference">i-> {return i;}</error>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -12,6 +12,6 @@ class TypeArgsConsistency {
|
||||
I<Integer> i1 = (i, j) -> i + j;
|
||||
foo((i, j) -> i + j);
|
||||
I<Integer> i2 = bar((i, j) -> i + j);
|
||||
I<Integer> i3 = bar<error descr="'bar(TypeArgsConsistency.I<java.lang.Integer>)' in 'TypeArgsConsistency' cannot be applied to '(<lambda expression>)'">((i, j) -> "" + i + j)</error>;
|
||||
<error descr="Incompatible types. Found: 'TypeArgsConsistency.I<java.lang.String>', required: 'TypeArgsConsistency.I<java.lang.Integer>'">I<Integer> i3 = bar((i, j) -> "" + i + j);</error>
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
public class NotAFIT {
|
||||
static class First {
|
||||
static interface A<T> {
|
||||
void foo1();
|
||||
void foo2();
|
||||
}
|
||||
static <T> void foo(A<T> a) {
|
||||
}
|
||||
|
||||
void bar() {
|
||||
foo(<error descr="Multiple non-overriding abstract methods found">() ->{}</error>);
|
||||
}
|
||||
}
|
||||
|
||||
static class WithInheritance {
|
||||
static interface A<T> {
|
||||
void foo1();
|
||||
}
|
||||
|
||||
static interface B<M> extends A<M> {
|
||||
void foo2();
|
||||
}
|
||||
|
||||
static <T> void foo(B<T> a) {
|
||||
}
|
||||
|
||||
void bar() {
|
||||
foo(<error descr="Multiple non-overriding abstract methods found">()->{}</error>);
|
||||
}
|
||||
}
|
||||
|
||||
static class WithInheritanceOverrideSameMethod {
|
||||
static interface A<T> {
|
||||
void foo1();
|
||||
}
|
||||
|
||||
static interface B<M> extends A<M> {
|
||||
void foo1();
|
||||
}
|
||||
|
||||
static <T> void foo(B<T> a) {
|
||||
}
|
||||
|
||||
void bar() {
|
||||
foo(()->{});
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
class NoLambda {
|
||||
interface I<T> {
|
||||
void f(T t);
|
||||
}
|
||||
|
||||
<Z> void bar(I<Z> iz) {
|
||||
}
|
||||
|
||||
void bazz() {
|
||||
bar(null);
|
||||
bar(<error descr="Cyclic inference">(z)-> {System.out.println();}</error>);
|
||||
}
|
||||
|
||||
static <T> T id(T i2) {return i2;}
|
||||
|
||||
{
|
||||
id(<error descr="Cyclic inference">() -> {System.out.println("hi");}</error>);
|
||||
NoLambda.<Runnable>id(() -> {System.out.println("hi");});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -30,6 +30,6 @@ class ReturnTypeCompatibility {
|
||||
|
||||
public static void main(String[] args) {
|
||||
call((String i)->{ return i;});
|
||||
call<error descr="'call(ReturnTypeCompatibility.I1<java.lang.Object>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
|
||||
call<error descr="'call(ReturnTypeCompatibility.I1<java.lang.Integer>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ interface SAM<X> {
|
||||
|
||||
class Foo {
|
||||
void test() {
|
||||
SAM<Integer> c = (i, j)->"" + i + j;
|
||||
SAM<String> c = (i, j)->"" + i + j;
|
||||
SAM<Integer> s3 = m(c);
|
||||
}
|
||||
<X> SAM<X> m(SAM<X> s) { return null; }
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.codeInsight.daemon;
|
||||
|
||||
import com.intellij.psi.LambdaUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class FunctionalInterfaceTest extends LightDaemonAnalyzerTestCase {
|
||||
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/functionalInterface";
|
||||
|
||||
private void doTestFunctionalInterface(@Nullable String expectedErrorMessage) throws Exception {
|
||||
String filePath = BASE_PATH + "/" + getTestName(false) + ".java";
|
||||
configureByFile(filePath);
|
||||
final PsiClass psiClass = getJavaFacade().findClass("Foo", GlobalSearchScope.projectScope(getProject()));
|
||||
assertNotNull("Class Foo not found", psiClass);
|
||||
|
||||
final String errorMessage = LambdaUtil.checkInterfaceFunctional(psiClass);
|
||||
assertEquals(expectedErrorMessage, errorMessage);
|
||||
}
|
||||
|
||||
public void testSimple() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testNoMethods() throws Exception {
|
||||
doTestFunctionalInterface("No target method found");
|
||||
}
|
||||
|
||||
public void testMultipleMethods() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testMultipleMethodsInOne() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testClone() throws Exception {
|
||||
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
|
||||
}
|
||||
|
||||
public void testTwoMethodsSameSignature() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testTwoMethodsSubSignature() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testTwoMethodsNoSubSignature() throws Exception {
|
||||
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
|
||||
}
|
||||
|
||||
public void testTwoMethodsNoSubSignature1() throws Exception {
|
||||
doTestFunctionalInterface("Multiple non-overriding abstract methods found");
|
||||
}
|
||||
|
||||
public void testTwoMethodsSameSubstSignature() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testMethodWithTypeParam() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testTwoMethodsSameSignatureTypeParams() throws Exception {
|
||||
doTestFunctionalInterface(null);
|
||||
}
|
||||
|
||||
public void testAbstractClass() throws Exception {
|
||||
doTestFunctionalInterface("Target type of a lambda conversion must be an interface");
|
||||
}
|
||||
}
|
||||
+4
@@ -48,6 +48,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
public void testWildcardBounds() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInferenceOnMethodCallSite() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAmbiguity1() throws Exception {
|
||||
doTest();
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.codeInsight.daemon.lambda;
|
||||
|
||||
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
public class LambdaInferenceTest extends LightDaemonAnalyzerTestCase {
|
||||
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/inference";
|
||||
|
||||
public void testFunctionalInterfaceShouldHaveExactlyOneMethod() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testSimpleCyclicInference() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
|
||||
}
|
||||
}
|
||||
@@ -364,7 +364,7 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
|
||||
}
|
||||
|
||||
public void testLambdaExprNotAccepted() throws Exception {
|
||||
doTest(new MockIntroduceVariableHandler("c", false, false, false, "SAM<java.lang.Integer>"));
|
||||
doTest(new MockIntroduceVariableHandler("c", false, false, false, "SAM<java.lang.String>"));
|
||||
}
|
||||
|
||||
private void doTest(IntroduceVariableBase testMe) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user