substitute type parameter bounds with current substitution when method signatures in the hierarchy are checked (IDEA-159801)

This commit is contained in:
Anna Kozlova
2016-09-15 18:35:33 +03:00
parent 596430d19c
commit 92e17eed9c
5 changed files with 80 additions and 44 deletions
@@ -1302,16 +1302,16 @@ public class HighlightMethodUtil {
private static HighlightInfo checkInterfaceInheritedMethodsReturnTypes(@NotNull List<? extends MethodSignatureBackedByPsiMethod> superMethodSignatures,
@NotNull LanguageLevel languageLevel) {
if (superMethodSignatures.size() < 2) return null;
MethodSignatureBackedByPsiMethod returnTypeSubstitutable = superMethodSignatures.get(0);
final MethodSignatureBackedByPsiMethod[] returnTypeSubstitutable = {superMethodSignatures.get(0)};
for (int i = 1; i < superMethodSignatures.size(); i++) {
PsiMethod currentMethod = returnTypeSubstitutable.getMethod();
PsiType currentType = returnTypeSubstitutable.getSubstitutor().substitute(currentMethod.getReturnType());
PsiMethod currentMethod = returnTypeSubstitutable[0].getMethod();
PsiType currentType = returnTypeSubstitutable[0].getSubstitutor().substitute(currentMethod.getReturnType());
MethodSignatureBackedByPsiMethod otherSuperSignature = superMethodSignatures.get(i);
PsiMethod otherSuperMethod = otherSuperSignature.getMethod();
PsiType otherSuperReturnType = otherSuperSignature.getSubstitutor().substitute(otherSuperMethod.getReturnType());
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable,
PsiSubstitutor otherSubstitutor = otherSuperSignature.getSubstitutor();
PsiType otherSuperReturnType = otherSubstitutor.substitute(otherSuperMethod.getReturnType());
PsiSubstitutor unifyingSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(returnTypeSubstitutable[0],
otherSuperSignature);
if (unifyingSubstitutor != null) {
otherSuperReturnType = unifyingSubstitutor.substitute(otherSuperReturnType);
@@ -1319,20 +1319,25 @@ public class HighlightMethodUtil {
}
if (otherSuperReturnType == null || currentType == null || otherSuperReturnType.equals(currentType)) continue;
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
//http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
if (!(otherSuperReturnType instanceof PsiPrimitiveType || currentType instanceof PsiPrimitiveType)) {
if (otherSuperReturnType.isAssignableFrom(currentType)) continue;
if (currentType.isAssignableFrom(otherSuperReturnType)) {
returnTypeSubstitutable = otherSuperSignature;
continue;
PsiType otherReturnType = otherSuperReturnType;
PsiType curType = currentType;
final HighlightInfo info =
LambdaUtil.performWithSubstitutedParameterBounds(otherSuperMethod.getTypeParameters(), otherSubstitutor, () -> {
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5)) {
//http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8 Example 8.1.5-3
if (!(otherReturnType instanceof PsiPrimitiveType || curType instanceof PsiPrimitiveType)) {
if (otherReturnType.isAssignableFrom(curType)) return null;
if (curType.isAssignableFrom(otherReturnType)) {
returnTypeSubstitutable[0] = otherSuperSignature;
return null;
}
}
if (otherSuperMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(curType, otherReturnType)) return null;
}
}
if (otherSuperMethod.getTypeParameters().length > 0 && JavaGenericsUtil.isRawToGeneric(currentType, otherSuperReturnType)) continue;
}
return createIncompatibleReturnTypeMessage(otherSuperMethod, currentMethod, currentType, otherSuperReturnType,
JavaErrorMessages.message("unrelated.overriding.methods.return.types"), TextRange.EMPTY_RANGE);
return createIncompatibleReturnTypeMessage(otherSuperMethod, currentMethod, curType, otherReturnType,
JavaErrorMessages.message("unrelated.overriding.methods.return.types"), TextRange.EMPTY_RANGE);
});
if (info != null) return info;
}
return null;
}
@@ -25,7 +25,10 @@ import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Producer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -821,6 +824,34 @@ public class LambdaUtil {
return copyCall;
}
public static <T> T performWithSubstitutedParameterBounds(final PsiTypeParameter[] typeParameters,
final PsiSubstitutor substitutor,
final Producer<T> producer) {
try {
for (PsiTypeParameter parameter : typeParameters) {
final PsiClassType[] types = parameter.getExtendsListTypes();
if (types.length > 0) {
final List<PsiType> conjuncts = ContainerUtil.map(types, new Function<PsiClassType, PsiType>() {
@Override
public PsiType fun(PsiClassType type) {
return substitutor.substitute(type);
}
});
//don't glb to avoid flattening = Object&Interface would be preserved
//otherwise methods with different signatures could get same erasure
final PsiType upperBound = PsiIntersectionType.createIntersection(false, conjuncts.toArray(new PsiType[conjuncts.size()]));
getFunctionalTypeMap().put(parameter, upperBound);
}
}
return producer.produce();
}
finally {
for (PsiTypeParameter parameter : typeParameters) {
getFunctionalTypeMap().remove(parameter);
}
}
}
public static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
@@ -32,6 +32,7 @@ import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.Producer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -1651,36 +1652,19 @@ public class InferenceSession {
/**
* 18.5.4 More Specific Method Inference
*/
public static boolean isMoreSpecific(PsiMethod m1,
public static boolean isMoreSpecific(final PsiMethod m1,
final PsiMethod m2,
final PsiSubstitutor siteSubstitutor1,
final PsiSubstitutor siteSubstitutor1,
final PsiExpression[] args,
final PsiElement context,
final boolean varargs) {
final PsiTypeParameter[] typeParameters = m1.getTypeParameters();
try {
for (PsiTypeParameter parameter : typeParameters) {
final PsiClassType[] types = parameter.getExtendsListTypes();
if (types.length > 0) {
final List<PsiType> conjuncts = ContainerUtil.map(types, new Function<PsiClassType, PsiType>() {
@Override
public PsiType fun(PsiClassType type) {
return siteSubstitutor1.substitute(type);
}
});
//don't glb to avoid flattening = Object&Interface would be preserved
//otherwise methods with different signatures could get same erasure
final PsiType upperBound = PsiIntersectionType.createIntersection(false, conjuncts.toArray(new PsiType[conjuncts.size()]));
LambdaUtil.getFunctionalTypeMap().put(parameter, upperBound);
}
return LambdaUtil.performWithSubstitutedParameterBounds(m1.getTypeParameters(), siteSubstitutor1, new Producer<Boolean>() {
@Nullable
@Override
public Boolean produce() {
return isMoreSpecificInternal(m1, m2, siteSubstitutor1, args, context, varargs);
}
return isMoreSpecificInternal(m1, m2, siteSubstitutor1, args, context, varargs);
}
finally {
for (PsiTypeParameter parameter : typeParameters) {
LambdaUtil.getFunctionalTypeMap().remove(parameter);
}
}
});
}
private static boolean isMoreSpecificInternal(PsiMethod m1,
@@ -0,0 +1,12 @@
import java.util.List;
interface A {
String save(String world);
}
interface Test2 extends A, CR<String> {}
interface Test1 extends CR<String>, A {}
interface CR<T> {
<S extends T> S save(S var1);
<S extends T> List<S> save(Iterable<S> var1);
}
@@ -1005,4 +1005,8 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase {
public void testMembersContainedInCapturedWildcardType() throws Exception {
doTest();
}
public void testTypeParameterBoundsWithSubstitutionWhenMethodHierarchyIsChecked() throws Exception {
doTest();
}
}