new inference: use site substitution inside one level of inference only so calls with different site substitutions are possible in one expression (IDEA-131723; IDEA-131562)

This commit is contained in:
Anna Kozlova
2014-10-24 18:36:20 +02:00
parent a9bb3f4122
commit 5136167d8b
6 changed files with 125 additions and 45 deletions
@@ -255,41 +255,55 @@ public class GenericsUtil {
final PsiSubstitutor substitutor,
final PsiElement context,
final boolean allowUncheckedConversion) {
nextTypeParam:
for (PsiTypeParameter typeParameter : typeParams) {
PsiType substituted = substitutor.substitute(typeParameter);
if (substituted == null) return null;
if (context != null) {
substituted = PsiUtil.captureToplevelWildcards(substituted, context);
PsiType boundError = findTypeParameterBoundError(typeParameter, typeParameter.getExtendsListTypes(),
substitutor, context, allowUncheckedConversion);
if (boundError != null) {
return Pair.create(typeParameter, boundError);
}
}
return null;
}
PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
for (PsiClassType type : extendsTypes) {
PsiType extendsType = substitutor.substitute(type);
if (substituted instanceof PsiWildcardType) {
if (((PsiWildcardType)substituted).isSuper()) {
continue;
}
final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
continue nextTypeParam;
}
public static PsiType findTypeParameterBoundError(PsiTypeParameter typeParameter,
PsiType[] extendsTypes,
PsiSubstitutor substitutor,
PsiElement context,
boolean allowUncheckedConversion) {
PsiType substituted = substitutor.substitute(typeParameter);
if (substituted == null) return null;
if (context != null) {
substituted = PsiUtil.captureToplevelWildcards(substituted, context);
}
if (substituted instanceof PsiWildcardType) {
if (((PsiWildcardType)substituted).isSuper()) {
return null;
}
}
for (PsiType type : extendsTypes) {
PsiType extendsType = substitutor.substitute(type);
if (substituted instanceof PsiWildcardType) {
final PsiType extendsBound = ((PsiWildcardType)substituted).getExtendsBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
return null;
}
else if (substituted instanceof PsiIntersectionType) {
for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) {
if (acceptExtendsBound(extendsType, extendsBound)) continue nextTypeParam;
}
}
else if (substituted instanceof PsiIntersectionType) {
for (PsiType extendsBound : ((PsiIntersectionType)substituted).getConjuncts()) {
if (acceptExtendsBound(extendsType, extendsBound)) return null;
}
else if (substituted instanceof PsiCapturedWildcardType) {
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
continue nextTypeParam;
}
}
if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) {
return Pair.create(typeParameter, extendsType);
}
else if (substituted instanceof PsiCapturedWildcardType) {
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
return null;
}
}
if (extendsType != null && !TypeConversionUtil.isAssignable(extendsType, substituted, allowUncheckedConversion)) {
return extendsType;
}
}
return null;
}
@@ -254,12 +254,12 @@ public class InferenceSession {
}
}
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, mySiteSubstitutor);
final PsiSubstitutor substitutor = resolveBounds(myInferenceVariables, PsiSubstitutor.EMPTY);
if (substitutor != null) {
if (myContext != null) {
myContext.putUserData(ERASED, myErased);
}
mySiteSubstitutor = substitutor;
mySiteSubstitutor = mySiteSubstitutor.putAll(substitutor);
for (InferenceVariable variable : myInferenceVariables) {
variable.setInstantiation(substitutor.substitute(variable.getParameter()));
}
@@ -403,7 +403,7 @@ public class InferenceSession {
}
public PsiSubstitutor retrieveNonPrimitiveEqualsBounds(Collection<InferenceVariable> variables) {
PsiSubstitutor substitutor = mySiteSubstitutor;
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
for (InferenceVariable variable : variables) {
final PsiType equalsBound = getEqualsBound(variable, substitutor);
if (!(equalsBound instanceof PsiPrimitiveType)) {
@@ -751,6 +751,22 @@ public class InferenceSession {
return isProperType(bound) ? bound : substitutor.substitute(bound);
}
private static boolean hasBoundProblems(final List<InferenceVariable> typeParams,
final PsiSubstitutor substitutor,
final PsiElement context) {
for (InferenceVariable typeParameter : typeParams) {
if (typeParameter.getCallContext() != context) {
continue;
}
final List<PsiType> extendsTypes = typeParameter.getBounds(InferenceBound.UPPER);
final PsiType[] bounds = extendsTypes.toArray(new PsiType[extendsTypes.size()]);
if (GenericsUtil.findTypeParameterBoundError(typeParameter, bounds, substitutor, context, true) != null) {
return true;
}
}
return false;
}
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables,
PsiSubstitutor substitutor) {
final Collection<InferenceVariable> allVars = new ArrayList<InferenceVariable>(inferenceVariables);
@@ -759,8 +775,7 @@ public class InferenceSession {
if (!myIncorporationPhase.hasCaptureConstraints(vars)) {
PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor);
if (firstSubstitutor != null) {
final Set<PsiTypeParameter> parameters = firstSubstitutor.getSubstitutionMap().keySet();
if (GenericsUtil.findTypeParameterWithBoundError(parameters.toArray(new PsiTypeParameter[parameters.size()]), firstSubstitutor, myContext, true) != null) {
if (hasBoundProblems(vars, firstSubstitutor, myContext)) {
firstSubstitutor = null;
}
}
@@ -837,7 +852,7 @@ public class InferenceSession {
for (InferenceVariable var : vars) {
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
final PsiTypeParameter typeParameter = var.getParameter();
if (substitutor.getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) {
if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().containsKey(typeParameter) && var.getCallContext() != myContext) {
continue;//todo
}
@@ -849,7 +864,7 @@ public class InferenceSession {
type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject()));
}
else {
if (substitutor.getSubstitutionMap().get(typeParameter) != null) continue;
if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(typeParameter) != null) continue;
type = myErased ? null : getUpperBound(var, substitutor);
}
}
@@ -90,13 +90,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (method != null && !method.isConstructor()) {
returnType = method.getReturnType();
if (returnType != null) {
List<PsiTypeParameter> params = new ArrayList<PsiTypeParameter>();
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(method)) {
params.add(parameter);
}
typeParams = params.toArray(new PsiTypeParameter[params.size()]);
typeParams = method.getTypeParameters();
}
} else if (myExpression instanceof PsiNewExpression) { //default constructor
} else if (myExpression instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)myExpression).getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiElement psiClass = classReference.resolve();
@@ -108,7 +104,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
if (typeParams != null) {
final InferenceSession callSession = new InferenceSession(typeParams, PsiSubstitutor.EMPTY, myExpression.getManager(), myExpression);
PsiSubstitutor siteSubstitutor =
resolveResult instanceof MethodCandidateInfo && method != null && !method.isConstructor() ? ((MethodCandidateInfo)resolveResult).getSiteSubstitutor() : PsiSubstitutor.EMPTY;
final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, myExpression.getManager(), myExpression);
callSession.propagateVariables(session.getInferenceVariables());
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
@@ -120,7 +118,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (!accepted) {
return false;
}
callSession.registerReturnTypeConstraints(returnType, myT);
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), myT);
if (callSession.repeatInferencePhases(true)) {
session.registerNestedSession(callSession);
} else {
@@ -0,0 +1,49 @@
import java.util.Map;
import java.util.List;
//expected type parameter
class Sample<V> {
public <K> Map<V, K> test() {
return null;
}
public <T> void fun(T t, V v) {
}
<M> M bar() {
return null;
}
void run(Sample<Integer> sample) {
fun(test(), bar());
sample.fun(test(), sample.bar());
sample.fun(test(), bar());
fun(test(), sample.bar());
fun(sample.test(), bar());
fun(sample.test(), sample.bar());
sample.fun(sample.test(), bar());
sample.fun(sample.test(), sample.bar());
}
}
//expected generic type
class Sample1<T> {
public <S extends T> List<S> reverse() {
return null;
}
public void foo(Sample1<Comparable> t)
{
newTreeSet(t.reverse());
newTreeSet(reverse());
t.newTreeSet(t.reverse());
t.newTreeSet(reverse());
}
public <E> void newTreeSet(List<E> comparator) {}
}
@@ -67,7 +67,7 @@ class MyTest1 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest1.m(I1)' and 'MyTest1.m(I2)' match">(Foo::new)</error>;
m<error descr="Ambiguous method call: both 'MyTest1.m(I2)' and 'MyTest1.m(I3)' match">(Foo::new)</error>;
}
}
class MyTest2 {
@@ -104,6 +104,6 @@ class MyTest2 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest2.m(I1)' and 'MyTest2.m(I2)' match">(Foo::new)</error>;
m<error descr="Ambiguous method call: both 'MyTest2.m(I2)' and 'MyTest2.m(I3)' match">(Foo::new)</error>;
}
}
@@ -172,6 +172,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testSiteSubstitutionInExpressionConstraints() throws Exception {
doTest();
}
public void testIncorporationWithEqualsBoundsSubstitution() throws Exception {
doTest();
}