method refs: check cyclic inference for method refs

This commit is contained in:
anna
2012-10-04 17:21:01 +02:00
parent 96bf64f976
commit 120111d01b
5 changed files with 61 additions and 10 deletions
@@ -977,6 +977,12 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (myRefCountHolder != null) {
myRefCountHolder.registerReference(expression, result);
}
if (!myHolder.hasErrorResults()) {
final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
if (LambdaUtil.dependsOnTypeParams(functionalInterfaceType, functionalInterfaceType, expression, null)) {
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, "Cyclic inference")); //todo[ann] append not inferred type params info
}
}
}
@Override
@@ -355,7 +355,7 @@ public class LambdaUtil {
public static boolean dependsOnTypeParams(PsiType type,
PsiType functionalInterfaceType,
PsiLambdaExpression lambdaExpression,
PsiElement lambdaExpression,
PsiTypeParameter param2Check) {
return depends(type, param2Check, new TypeParamsChecker(lambdaExpression,
PsiUtil.resolveClassInType(functionalInterfaceType)));
@@ -750,11 +750,11 @@ public class LambdaUtil {
myClass = aClass;
}
public TypeParamsChecker(PsiLambdaExpression expression) {
public TypeParamsChecker(PsiElement expression) {
this(expression, PsiUtil.resolveGenericsClassInType(getFunctionalInterfaceType(expression, false)).getElement());
}
public TypeParamsChecker(PsiLambdaExpression expression, PsiClass aClass) {
public TypeParamsChecker(PsiElement expression, PsiClass aClass) {
myClass = aClass;
PsiElement parent = expression.getParent();
while (parent instanceof PsiParenthesizedExpression) {
@@ -763,7 +763,11 @@ public class LambdaUtil {
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
if (gParent instanceof PsiCallExpression) {
myMethod = ((PsiCallExpression)gParent).resolveMethod();
final Map<PsiElement, PsiMethod> map = MethodCandidateInfo.CURRENT_CANDIDATE.get();
myMethod = map != null ? map.get(parent) : null;
if (myMethod == null) {
myMethod = ((PsiCallExpression)gParent).resolveMethod();
}
if (myMethod != null && PsiTreeUtil.isAncestor(myMethod, expression, false)) {
myMethod = null;
}
@@ -594,7 +594,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
}
@Nullable
private static Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(PsiTypeParameter typeParam,
private static Pair<PsiType, ConstraintType> inferConstraintFromFunctionalInterfaceMethod(final PsiTypeParameter typeParam,
final PsiMethodReferenceExpression methodReferenceExpression,
final PsiType functionalInterfaceType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
@@ -607,6 +607,10 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
methodParamTypes[i] = subst.substitute(methodParameters[i].getType());
}
if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) {
return null;
}
final PsiType[] args = new PsiType[methodParameters.length];
final PsiElement resolved = methodReferenceExpression.resolve();
if (resolved instanceof PsiMethod) {
@@ -698,12 +702,12 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
return null;
}
private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiLambdaExpression lambdaExpression,
private static boolean methodParamsDependOn(PsiTypeParameter typeParam, PsiElement psiElement,
PsiType functionalInterfaceType,
PsiParameter[] methodParameters,
PsiSubstitutor subst) {
for (PsiParameter parameter : methodParameters) {
if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, lambdaExpression, typeParam)) {
if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), functionalInterfaceType, psiElement, typeParam)) {
return true;
}
}
@@ -948,15 +952,15 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
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) {
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType((PsiLambdaExpression)expression, false);
if (expression instanceof PsiLambdaExpression || expression instanceof PsiMethodReferenceExpression) {
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(expression, false);
if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){
return getFailedInferenceConstraint(typeParameter);
}
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
if (method == null || methodParamsDependOn(typeParameter, (PsiLambdaExpression)expression,
if (method == null || methodParamsDependOn(typeParameter, expression,
functionalInterfaceType, method.getParameterList().getParameters(),
LambdaUtil.getSubstitutor(method, resolveResult))) {
return getFailedInferenceConstraint(typeParameter);
@@ -0,0 +1,33 @@
class MyTest<E> {
MyTest(E e) {
}
interface I<T> {
MyTest<T> _(T t);
}
static <Y> void bar(Y arg, I<Y> i) {
i._(arg);
}
static <Y> void bar(I<Y> i, Y arg) {
i._(arg);
}
static <Y> void bar(I<Y> i) {
i._(null);
}
public static void main(String[] args) {
I<String> i = MyTest::new;
bar("", MyTest<String>::new);
bar("", MyTest::new);
bar(MyTest<String>::new, "");
bar(MyTest::new, "");
bar(<error descr="Cyclic inference">MyTest::new</error>);
bar(<error descr="Cyclic inference">MyTest<String>::new</error>);
}
}
@@ -93,6 +93,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testCyclicInference() throws Exception {
doTest();
}
public void testReturnTypeSpecific() throws Exception {
doTest(true);
}