mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
method refs: propagate substitutor from method reference resolve to upper level inference (IDEA-91988)
This commit is contained in:
@@ -296,7 +296,10 @@ public class LambdaUtil {
|
||||
|
||||
@Nullable
|
||||
private static List<MethodSignature> findFunctionCandidates(PsiClass psiClass) {
|
||||
if (psiClass.isInterface()) {
|
||||
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) {
|
||||
@@ -594,7 +597,9 @@ public class LambdaUtil {
|
||||
|
||||
final JavaResolveResult result;
|
||||
try {
|
||||
if (map.put(methodReferenceExpression, left) != null) return false;
|
||||
if (map.put(methodReferenceExpression, left) != null) {
|
||||
return false;
|
||||
}
|
||||
result = methodReferenceExpression.advancedResolve(false);
|
||||
}
|
||||
finally {
|
||||
@@ -642,7 +647,7 @@ public class LambdaUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isReceiverType(@Nullable PsiClass aClass, PsiClass containingClass) {
|
||||
private static boolean isReceiverType(@Nullable PsiClass aClass, @Nullable PsiClass containingClass) {
|
||||
while (containingClass != null) {
|
||||
if (InheritanceUtil.isInheritorOrSelf(aClass, containingClass, true)) return true;
|
||||
containingClass = containingClass.getContainingClass();
|
||||
@@ -650,10 +655,11 @@ public class LambdaUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isReceiverType(PsiType receiverType, PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
|
||||
public static boolean isReceiverType(PsiType receiverType, @Nullable PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(receiverType));
|
||||
final PsiClass receiverClass = resolveResult.getElement();
|
||||
if (receiverClass != null && isReceiverType(receiverClass, containingClass)) {
|
||||
LOG.assertTrue(containingClass != null);
|
||||
return resolveResult.getSubstitutor().equals(psiSubstitutor) ||
|
||||
PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor) ||
|
||||
PsiUtil.isRawSubstitutor(receiverClass, resolveResult.getSubstitutor());
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -217,7 +218,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
if (nullPassed && currentSubstitution == null) return RAW_INFERENCE;
|
||||
} else if (argumentType instanceof PsiMethodReferenceType) {
|
||||
final PsiMethodReferenceExpression referenceExpression = ((PsiMethodReferenceType)argumentType).getExpression();
|
||||
currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, parameterType);
|
||||
currentSubstitution = inferConstraintFromFunctionalInterfaceMethod(typeParameter, referenceExpression, partialSubstitutor.substitute(parameterType));
|
||||
}
|
||||
else {
|
||||
currentSubstitution = getSubstitutionForTypeParameterConstraint(typeParameter, parameterType,
|
||||
@@ -601,13 +602,13 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
final PsiMethodReferenceExpression methodReferenceExpression,
|
||||
final PsiType functionalInterfaceType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (method != null) {
|
||||
final PsiSubstitutor subst = LambdaUtil.getSubstitutor(method, resolveResult);
|
||||
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
|
||||
final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (functionalInterfaceMethod != null) {
|
||||
final PsiSubstitutor subst = LambdaUtil.getSubstitutor(functionalInterfaceMethod, resolveResult);
|
||||
final PsiParameter[] methodParameters = functionalInterfaceMethod.getParameterList().getParameters();
|
||||
PsiType[] methodParamTypes = new PsiType[methodParameters.length];
|
||||
for (int i = 0; i < methodParameters.length; i++) {
|
||||
methodParamTypes[i] = subst.substitute(methodParameters[i].getType());
|
||||
methodParamTypes[i] = GenericsUtil.eliminateWildcards(subst.substitute(methodParameters[i].getType()));
|
||||
}
|
||||
|
||||
if (methodParamsDependOn(typeParam, methodReferenceExpression, functionalInterfaceType, methodParameters, subst)) {
|
||||
@@ -615,20 +616,46 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
}
|
||||
|
||||
final PsiType[] args = new PsiType[methodParameters.length];
|
||||
final PsiElement resolved = methodReferenceExpression.resolve();
|
||||
if (resolved instanceof PsiMethod) {
|
||||
final PsiParameter[] parameters = ((PsiMethod)resolved).getParameterList().getParameters();
|
||||
if (parameters.length != methodParameters.length) return null;
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
args[i] = subst.substitute(parameters[i].getType());
|
||||
Map<PsiMethodReferenceExpression,PsiType> map = LambdaUtil.ourRefs.get();
|
||||
if (map == null) {
|
||||
map = new HashMap<PsiMethodReferenceExpression, PsiType>();
|
||||
LambdaUtil.ourRefs.set(map);
|
||||
}
|
||||
final PsiType added = map.put(methodReferenceExpression, functionalInterfaceType);
|
||||
final JavaResolveResult methReferenceResolveResult;
|
||||
try {
|
||||
methReferenceResolveResult = methodReferenceExpression.advancedResolve(false);
|
||||
}
|
||||
finally {
|
||||
if (added == null) {
|
||||
map.remove(methodReferenceExpression);
|
||||
}
|
||||
final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, methodParamTypes, args, subst, null,
|
||||
DefaultParameterTypeInferencePolicy.INSTANCE);
|
||||
}
|
||||
final PsiElement resolved = methReferenceResolveResult.getElement();
|
||||
if (resolved instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)resolved;
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
boolean hasReceiver = false;
|
||||
if (methodParamTypes.length == parameters.length + 1) {
|
||||
if (!LambdaUtil.isReceiverType(methodParamTypes[0], method.getContainingClass(), methReferenceResolveResult.getSubstitutor())) return null;
|
||||
hasReceiver = true;
|
||||
} else if (parameters.length != methodParameters.length) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
args[i] = methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(parameters[i].getType()));
|
||||
}
|
||||
|
||||
final PsiType[] typesToInfer = hasReceiver ? ArrayUtil.remove(methodParamTypes, 0) : methodParamTypes;
|
||||
final Pair<PsiType, ConstraintType> constraint = inferTypeForMethodTypeParameterInner(typeParam, typesToInfer, args, subst, null, DefaultParameterTypeInferencePolicy.INSTANCE);
|
||||
if (constraint != null){
|
||||
return constraint;
|
||||
}
|
||||
return getSubstitutionForTypeParameterConstraint(typeParam, GenericsUtil.eliminateWildcards(subst.substitute(method.getReturnType())),
|
||||
((PsiMethod)resolved).getReturnType(), true, PsiUtil.getLanguageLevel(method));
|
||||
PsiType functionalInterfaceReturnType = functionalInterfaceMethod.getReturnType();
|
||||
if (functionalInterfaceReturnType != null && functionalInterfaceReturnType != PsiType.VOID) {
|
||||
functionalInterfaceReturnType = GenericsUtil.eliminateWildcards(subst.substitute(functionalInterfaceReturnType));
|
||||
return getSubstitutionForTypeParameterConstraint(typeParam, functionalInterfaceReturnType, methReferenceResolveResult.getSubstitutor().substitute(subst.substitute(method.getReturnType())), true, PsiUtil.getLanguageLevel(functionalInterfaceMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -385,6 +385,9 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
return PsiUtil.resolveGenericsClassInType(types[0]).getSubstitutor();
|
||||
}
|
||||
|
||||
for (int i = 0; i < rightTypes.length; i++) {
|
||||
rightTypes[i] = GenericsUtil.eliminateWildcards(rightTypes[i]);
|
||||
}
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(getProject()).getResolveHelper();
|
||||
PsiSubstitutor psiSubstitutor = resolveHelper.inferTypeArguments(method.getTypeParameters(), types, rightTypes, languageLevel);
|
||||
psiSubstitutor = psiSubstitutor.putAll(substitutor);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
class Test {
|
||||
static <U> Iterable<U> map(Mapper<? super String, ? extends U> mapper) {
|
||||
return null;
|
||||
}
|
||||
|
||||
static void test() {
|
||||
Integer next = map(String::length).iterator().next();
|
||||
Integer next1 = map(Test::length).iterator().next();
|
||||
}
|
||||
|
||||
public static <T> T length(T s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> int length(String s) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
interface Mapper<T, U> {
|
||||
U map(T t);
|
||||
}
|
||||
@@ -117,6 +117,14 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInferenceFromMethodReference() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testAssignability1() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testInferenceFromReturnType() throws Exception {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user