lambda: check formal params for equality, eliminate wildcards during inference according to 15.27.3

This commit is contained in:
Anna Kozlova
2013-09-23 19:40:42 +04:00
parent fc9a196883
commit e78ab513a5
7 changed files with 155 additions and 15 deletions

View File

@@ -30,6 +30,7 @@ import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
@@ -37,6 +38,7 @@ import com.intellij.psi.*;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTagValue;
@@ -299,7 +301,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(result);
}
else {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(FunctionalInterfaceParameterizationUtil.getFunctionalType(functionalInterfaceType, expression));
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod != null) {
final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters();
@@ -311,16 +313,28 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(result);
}
else {
for (int i = 0; i < lambdaParameters.length; i++) {
PsiParameter lambdaParameter = lambdaParameters[i];
if (!TypeConversionUtil.isAssignable(lambdaParameter.getType(),
GenericsUtil.eliminateWildcards(
LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)
.substitute(parameters[i].getType())))) {
HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(lambdaParameter)
.descriptionAndTooltip(incompatibleTypesMessage).create();
myHolder.add(result);
break;
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
if (expression.hasFormalParameterTypes()) {
for (int i = 0; i < lambdaParameters.length; i++) {
if (!Comparing.equal(lambdaParameters[i].getType(), substitutor.substitute(parameters[i].getType()))) {
HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(lambdaParameters[i])
.descriptionAndTooltip(incompatibleTypesMessage)
.create();
myHolder.add(result);
break;
}
}
} else {
for (int i = 0; i < lambdaParameters.length; i++) {
PsiParameter lambdaParameter = lambdaParameters[i];
if (!TypeConversionUtil.isAssignable(lambdaParameter.getType(),
GenericsUtil.eliminateWildcards(substitutor.substitute(parameters[i].getType())))) {
HighlightInfo result = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(lambdaParameter)
.descriptionAndTooltip(incompatibleTypesMessage).create();
myHolder.add(result);
break;
}
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeEqualityConstraint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FunctionalInterfaceParameterizationUtil {
private static final Logger LOG = Logger.getInstance("#" + FunctionalInterfaceParameterizationUtil.class.getName());
public static boolean isWildcardParameterized(@NotNull PsiType classType) {
if (classType instanceof PsiIntersectionType) {
for (PsiType type : ((PsiIntersectionType)classType).getConjuncts()) {
if (!isWildcardParameterized(type)) return false;
}
}
if (classType instanceof PsiClassType) {
for (PsiType type : ((PsiClassType)classType).getParameters()) {
if (type instanceof PsiWildcardType || type instanceof PsiCapturedWildcardType) {
return true;
}
}
return false;
}
LOG.error("Unexpected type: " + classType);
return false;
}
@Nullable
public static PsiType getFunctionalType(PsiType psiClassType, PsiLambdaExpression expr) {
if (!expr.hasFormalParameterTypes()) return psiClassType;
if (!isWildcardParameterized(psiClassType)) {
return psiClassType;
}
final PsiParameter[] lambdaParams = expr.getParameterList().getParameters();
if (psiClassType instanceof PsiIntersectionType) {
for (PsiType psiType : ((PsiIntersectionType)psiClassType).getConjuncts()) {
final PsiType functionalType = getFunctionalType(psiType, expr);
if (functionalType != null) return functionalType;
}
return null;
}
LOG.assertTrue(psiClassType instanceof PsiClassType, "Unexpected type: " + psiClassType);
final PsiType[] parameters = ((PsiClassType)psiClassType).getParameters();
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)psiClassType).resolveGenerics();
PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) return null;
final InferenceSession session = new InferenceSession(PsiSubstitutor.EMPTY);
PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
if (typeParameters.length != parameters.length) {
return null;
}
for (int i = 0; i < typeParameters.length; i++) {
session.addVariable(typeParameters[i], parameters[i]);
}
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
final PsiParameter[] targetMethodParams = interfaceMethod.getParameterList().getParameters();
for (int i = 0; i < targetMethodParams.length; i++) {
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
}
final PsiClassType parameterization = elementFactory.createType(psiClass, session.infer());
if (!isWildcardParameterized(parameterization)) return parameterization;
}
return null;
}
}

View File

@@ -48,6 +48,10 @@ public class InferenceSession {
private final InferenceIncorporationPhase myIncorporationPhase = new InferenceIncorporationPhase(this);
public InferenceSession(PsiSubstitutor siteSubstitutor) {
mySiteSubstitutor = siteSubstitutor;
}
public InferenceSession(PsiTypeParameter[] typeParams,
PsiType[] leftTypes,
PsiType[] rightTypes,
@@ -80,7 +84,7 @@ public class InferenceSession {
PsiType parameterType = getParameterType(parameters, args, i, mySiteSubstitutor);
if (args[i] != null) {
myConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType));
myConstraints.add(new CheckedExceptionCompatibilityConstraint(args[i], parameterType));
//myConstraints.add(new CheckedExceptionCompatibilityConstraint(args[i], parameterType));
}
}
}
@@ -385,4 +389,20 @@ public class InferenceSession {
public Collection<PsiTypeParameter> getTypeParams() {
return myInferenceVariables.keySet();
}
public void addVariable(PsiTypeParameter typeParameter, final PsiType parameter) {
InferenceVariable variable = new InferenceVariable(typeParameter);
if (parameter instanceof PsiWildcardType) {
PsiType bound = ((PsiWildcardType)parameter).getBound();
if (bound != null) {
variable.addBound(bound, ((PsiWildcardType)parameter).isExtends() ? InferenceBound.UPPER : InferenceBound.LOWER);
} else {
variable.addBound(PsiType.getJavaLangObject(typeParameter.getManager(), parameter.getResolveScope()),
InferenceBound.UPPER);
}
} else {
variable.addBound(parameter, InferenceBound.EQ);
}
myInferenceVariables.put(typeParameter, variable);
}
}

View File

@@ -1,6 +1,7 @@
package com.intellij.psi.impl.source.resolve.graphInference.constraints;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.util.PsiUtil;
@@ -26,11 +27,13 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
if (myExpression.hasFormalParameterTypes()) {
}
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(myT);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(
FunctionalInterfaceParameterizationUtil.getFunctionalType(myT, myExpression));
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) {
return false;
}
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, PsiUtil.resolveGenericsClassInType(myT));
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
final PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters();
final PsiParameter[] lambdaParameters = myExpression.getParameterList().getParameters();

View File

@@ -0,0 +1,11 @@
public class Test {
interface Predicate<T> {
boolean test(T t);
}
{
Predicate<? super Integer> p = (Number n) -> n.equals(23);
Predicate<Integer> p1 = (<error descr="Incompatible parameter types in lambda expression">Number n</error>) -> n.equals(23);
Predicate<Number> p2 = (Number n) -> n.equals(23);
}
}

View File

@@ -32,6 +32,6 @@ class ReturnTypeCompatibility {
call((String i)->{ return i;});
call(<error descr="Cyclic inference">i->{ return i;}</error>);
call(<error descr="Cyclic inference">i->""</error>);
call<error descr="'call(ReturnTypeCompatibility.I1<java.lang.Integer>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
call((<error descr="Incompatible parameter types in lambda expression">int i</error>)->{ return i;});
}
}

View File

@@ -92,6 +92,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testFunctionalInterfaceCheck() { doTest();}
public void testUnderscores() { doTest(true);}
public void testReturnTypeAmbiguity() { doTest();}
public void testWildcardsAndFormalLambdaParams() {doTest();}
private void doTest() {
doTest(false);