new inference: collect during inference substitutor from inference variables and replace them with type parameters at the very end, excluding foreign variables so e.g. no essential dependency would get overridden by type parameter from sibling call (IDEA-140387)

This commit is contained in:
Anna Kozlova
2015-06-03 20:08:46 +02:00
parent a7e3ea78b8
commit 9bc2885c4c
5 changed files with 37 additions and 54 deletions

View File

@@ -264,7 +264,7 @@ public class InferenceSession {
if (parameters != null && args != null) {
final Set<ConstraintFormula> additionalConstraints = new LinkedHashSet<ConstraintFormula>();
if (parameters.length > 0) {
collectAdditionalConstraints(parameters, args, properties.getMethod(), PsiSubstitutor.EMPTY, additionalConstraints, properties.isVarargs());
collectAdditionalConstraints(parameters, args, properties.getMethod(), mySiteSubstitutor, additionalConstraints, properties.isVarargs());
}
if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) {
@@ -278,9 +278,22 @@ public class InferenceSession {
if (myContext != null) {
myContext.putUserData(ERASED, myErased);
}
mySiteSubstitutor = mySiteSubstitutor.putAll(substitutor);
for (InferenceVariable variable : myInferenceVariables) {
variable.setInstantiation(substitutor.substitute(variable.getParameter()));
final Map<PsiTypeParameter, PsiType> map = substitutor.getSubstitutionMap();
for (PsiTypeParameter parameter : map.keySet()) {
final PsiType mapping = map.get(parameter);
PsiTypeParameter param;
if (parameter instanceof InferenceVariable) {
((InferenceVariable)parameter).setInstantiation(mapping);
if (((InferenceVariable)parameter).getCallContext() != myContext) {
//don't include in result substitutor foreign inference variables
continue;
}
param = ((InferenceVariable)parameter).getParameter();
}
else {
param = parameter;
}
mySiteSubstitutor = mySiteSubstitutor.put(param, mapping);
}
} else {
return prepareSubstitution();
@@ -805,19 +818,15 @@ public class InferenceSession {
for (InferenceVariable dependency : dependencies) {
PsiType instantiation = dependency.getInstantiation();
if (instantiation != PsiType.NULL) {
substitutor = substitutor.put(dependency.getParameter(), instantiation);
substitutor = substitutor.put(dependency, instantiation);
}
}
return substitutor.substitute(bound);
}
private boolean hasBoundProblems(final List<InferenceVariable> typeParams,
final PsiSubstitutor psiSubstitutor,
final PsiSubstitutor substitutor) {
for (InferenceVariable typeParameter : typeParams) {
if (isForeignVariable(psiSubstitutor, typeParameter)) {
continue;
}
final List<PsiType> extendsTypes = typeParameter.getBounds(InferenceBound.UPPER);
final PsiType[] bounds = extendsTypes.toArray(new PsiType[extendsTypes.size()]);
if (GenericsUtil.findTypeParameterBoundError(typeParameter, bounds, substitutor, myContext, true) != null) {
@@ -827,36 +836,19 @@ public class InferenceSession {
return false;
}
private boolean isForeignVariable(PsiSubstitutor fullSubstitutor,
InferenceVariable typeParameter) {
return fullSubstitutor.putAll(mySiteSubstitutor).getSubstitutionMap().containsKey(typeParameter.getParameter()) &&
typeParameter.getCallContext() != myContext;
}
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables,
PsiSubstitutor substitutor) {
final Collection<InferenceVariable> allVars = new ArrayList<InferenceVariable>(inferenceVariables);
final Map<InferenceVariable, PsiType> foreignMap = new LinkedHashMap<InferenceVariable, PsiType>();
while (!allVars.isEmpty()) {
final List<InferenceVariable> vars = InferenceVariablesOrder.resolveOrder(allVars, this);
if (!myIncorporationPhase.hasCaptureConstraints(vars)) {
PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor, foreignMap);
if (firstSubstitutor != null) {
if (hasBoundProblems(vars, substitutor, firstSubstitutor)) {
firstSubstitutor = null;
}
PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor);
if (firstSubstitutor != null && hasBoundProblems(vars, firstSubstitutor)) {
firstSubstitutor = null;
}
if (firstSubstitutor != null) {
substitutor = firstSubstitutor;
allVars.removeAll(vars);
for (InferenceVariable var : vars) {
PsiType type = foreignMap.get(var);
if (type != null) {
var.setInstantiation(type);
}
}
continue;
}
}
@@ -924,29 +916,11 @@ public class InferenceSession {
}
private PsiSubstitutor resolveSubset(Collection<InferenceVariable> vars, PsiSubstitutor substitutor) {
return resolveSubset(vars, substitutor, null);
}
private PsiSubstitutor resolveSubset(Collection<InferenceVariable> vars,
PsiSubstitutor substitutor,
Map<InferenceVariable, PsiType> foreignMap) {
for (InferenceVariable var : vars) {
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
final PsiTypeParameter typeParameter = var.getParameter();
final PsiType type = checkBoundsConsistency(substitutor, var);
if (type != PsiType.NULL) {
if (foreignMap != null) {
//save all instantiations in a map where inference variables are not merged by type parameters
//for same method called with different args resulting in different inferred types
foreignMap.put(var, type);
}
if (isForeignVariable(substitutor, var)) {
continue;
}
substitutor = substitutor.put(typeParameter, type);
substitutor = substitutor.put(var, type);
}
}
@@ -978,7 +952,6 @@ public class InferenceSession {
type = PsiType.getJavaLangRuntimeException(myManager, GlobalSearchScope.allScope(myManager.getProject()));
}
else {
if (substitutor.putAll(mySiteSubstitutor).getSubstitutionMap().get(var.getParameter()) != null) return PsiType.NULL;
type = myErased ? null : upperBound;
}
}

View File

@@ -126,10 +126,6 @@ public class MethodReferenceResolver implements ResolveCache.PolyVariantContextR
}
if (includeReturnConstraint && interfaceMethodReturnType != PsiType.VOID && interfaceMethodReturnType != null) {
if (method.isConstructor()) {
//todo
session.initBounds(reference, method.getContainingClass().getTypeParameters());
}
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType();
if (returnType != null) {
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);

View File

@@ -0,0 +1,10 @@
import java.util.*;
abstract class Test {
{
hasEntry(equalTo("parentId"), equalTo(1));
}
public abstract <T> List<T> equalTo(T operand);
public abstract <K, V> void hasEntry(List<? super K> keyMatcher, List<? super V> valueMatcher);
}

View File

@@ -49,7 +49,7 @@ class MyTestConstructor {
private static void <warning descr="Private method 'foo(MyTestConstructor.I3)' is never used">foo</warning>(I3 i) {System.out.println(i);}
static {
foo<error descr="Cannot resolve method 'foo(<method reference>)'">(Foo::new)</error>;
foo<error descr="Ambiguous method call: both 'MyTestConstructor.foo(I1)' and 'MyTestConstructor.foo(I2)' match">(Foo::new)</error>;
}
}

View File

@@ -39,6 +39,10 @@ public class Java8ExpressionsCheckTest extends LightDaemonAnalyzerTestCase {
doTestAllMethodCallExpressions();
}
public void testInfinitiveParameterBoundsCheck() throws Exception {
doTestAllMethodCallExpressions();
}
private void doTestAllMethodCallExpressions() {
configureByFile(BASE_PATH + "/" + getTestName(false) + ".java");
final Collection<PsiCallExpression> methodCallExpressions = PsiTreeUtil.findChildrenOfType(getFile(), PsiCallExpression.class);