method refs: ensure raw substitutor is not used for second search case to build return type constraints (IDEA-165239)

This commit is contained in:
Anna.Kozlova
2016-12-14 11:31:36 +01:00
parent cb0636749a
commit 575a0740a9
4 changed files with 41 additions and 13 deletions
@@ -74,7 +74,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiClass applicableMemberContainingClass = applicableMember.getContainingClass();
final PsiClass containingClass = qualifierResolveResult.getContainingClass();
PsiSubstitutor psiSubstitutor = getSubstitutor(signature, qualifierResolveResult, applicableMember, applicableMemberContainingClass);
PsiSubstitutor psiSubstitutor = getSubstitutor(signature, qualifierResolveResult, applicableMember, applicableMemberContainingClass, myExpression);
int idx = 0;
for (PsiTypeParameter param : ((PsiTypeParameterListOwner)applicableMember).getTypeParameters()) {
@@ -167,7 +167,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiType referencedMethodReturnType;
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null, method);
PsiSubstitutor psiSubstitutor = getSubstitutor(signature, qualifierResolveResult, method, containingClass);
PsiSubstitutor psiSubstitutor = getSubstitutor(signature, qualifierResolveResult, method, containingClass, myExpression);
if (method.isConstructor()) {
referencedMethodReturnType = JavaPsiFacade.getElementFactory(method.getProject()).createType(containingClass, PsiSubstitutor.EMPTY);
@@ -218,19 +218,22 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
return true;
}
private PsiSubstitutor getSubstitutor(MethodSignature signature,
PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
PsiMember member,
@Nullable PsiClass containingClass) {
public static PsiSubstitutor getSubstitutor(MethodSignature signature,
PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
PsiMember member,
@Nullable PsiClass containingClass,
final PsiMethodReferenceExpression methodReferenceExpression) {
final PsiClass qContainingClass = qualifierResolveResult.getContainingClass();
PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor();
if (qContainingClass != null && containingClass != null) {
// 15.13.1 If the ReferenceType is a raw type, and there exists a parameterization of this type, T, that is a supertype of P1,
// the type to search is the result of capture conversion (5.1.10) applied to T;
// otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference.
if ( PsiUtil.isRawSubstitutor(qContainingClass, psiSubstitutor)) {
if (member instanceof PsiMethod && PsiMethodReferenceUtil.isSecondSearchPossible(signature.getParameterTypes(), qualifierResolveResult, myExpression)) {
final PsiType pType = PsiUtil.captureToplevelWildcards(signature.getParameterTypes()[0], myExpression);
if (PsiUtil.isRawSubstitutor(qContainingClass, psiSubstitutor)) {
if (member instanceof PsiMethod &&
PsiMethodReferenceUtil.isSecondSearchPossible(signature.getParameterTypes(), qualifierResolveResult,
methodReferenceExpression)) {
final PsiType pType = PsiUtil.captureToplevelWildcards(signature.getParameterTypes()[0], methodReferenceExpression);
psiSubstitutor = getParameterizedTypeSubstitutor(qContainingClass, pType);
}
else if (member instanceof PsiMethod && ((PsiMethod)member).isConstructor() || member instanceof PsiClass) {
@@ -238,16 +241,16 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
//If ClassType is a raw type, but is not a non-static member type of a raw type,
//the candidate notional member methods are those specified in §15.9.3 for a class instance creation expression that uses <>
//to elide the type arguments to a class.
final PsiResolveHelper helper = JavaPsiFacade.getInstance(myExpression.getProject()).getResolveHelper();
final PsiResolveHelper helper = JavaPsiFacade.getInstance(methodReferenceExpression.getProject()).getResolveHelper();
final PsiType[] paramTypes =
member instanceof PsiMethod ? ((PsiMethod)member).getSignature(PsiSubstitutor.EMPTY).getParameterTypes() : PsiType.EMPTY_ARRAY;
LOG.assertTrue(paramTypes.length == signature.getParameterTypes().length, "expr: " + myExpression + "; " +
LOG.assertTrue(paramTypes.length == signature.getParameterTypes().length, "expr: " + methodReferenceExpression + "; " +
paramTypes.length + "; " +
Arrays.toString(signature.getParameterTypes()));
psiSubstitutor = helper.inferTypeArguments(qContainingClass.getTypeParameters(),
paramTypes,
signature.getParameterTypes(),
PsiUtil.getLanguageLevel(myExpression));
PsiUtil.getLanguageLevel(methodReferenceExpression));
}
else {
psiSubstitutor = PsiSubstitutor.EMPTY;
@@ -21,6 +21,7 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
import com.intellij.psi.impl.source.resolve.graphInference.constraints.PsiMethodReferenceCompatibilityConstraint;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.ClassCandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -127,7 +128,8 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
}
if (includeReturnConstraint && !PsiType.VOID.equals(interfaceMethodReturnType) && interfaceMethodReturnType != null) {
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : substitutor.substitute(method.getReturnType());
PsiSubstitutor subst = PsiMethodReferenceCompatibilityConstraint.getSubstitutor(signature, qualifierResolveResult, method, containingClass, reference);
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, subst) : subst.substitute(method.getReturnType());
if (returnType != null) {
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);
}
@@ -0,0 +1,19 @@
class Test {
public interface Copier<TypeT> {
TypeT m(TypeT value);
}
public static class A<PhantomT> {
public <OtherT extends A<?>> OtherT copy() {
return null;
}
}
static <TypeT> void foo(TypeT value, Copier<TypeT> copier) {}
public static void foo() {
A<?> val = new A<>();
foo(val, A::copy);
}
}
@@ -529,6 +529,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testReturnTypeCompatibilityConstraintForSecondSearchCase() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}