mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
lambda: check formal params for equality, eliminate wildcards during inference according to 15.27.3
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user