method refs: propagate substitutor from method reference resolve to upper level inference (IDEA-91988)

This commit is contained in:
anna
2012-10-11 18:15:30 +02:00
parent fedfbcaa85
commit d598293eb9
5 changed files with 85 additions and 20 deletions

View File

@@ -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());

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}