new inference: target type detection according to consequence inference (IDEA-120193)

This commit is contained in:
Anna Kozlova
2014-02-04 12:29:51 +04:00
parent 834e582bd3
commit 8cca960f72
7 changed files with 137 additions and 24 deletions
@@ -27,6 +27,7 @@ import com.intellij.psi.impl.source.resolve.graphInference.constraints.*;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
@@ -304,25 +305,16 @@ public class InferenceSession {
returnType = PsiImplUtil.normalizeWildcardTypeByPosition(returnType, context);
PsiType targetType = PsiTypesUtil.getExpectedTypeByParent(context);
if (targetType == null) {
final PsiType finalReturnType = returnType;
targetType = PsiResolveHelper.ourGraphGuard.doPreventingRecursion(context, true, new Computable<PsiType>() {
@Override
public PsiType compute() {
return getTargetType(context);
return getTargetType(context, finalReturnType);
}
});
}
if (targetType != null) {
final InferenceVariable inferenceVariable = shouldResolveAndInstantiate(returnType, targetType);
if (inferenceVariable != null) {
resolveBounds(Collections.singletonList(inferenceVariable), mySiteSubstitutor, true);
myConstraints.add(new TypeCompatibilityConstraint(inferenceVariable.getInstantiation(), returnType));
}
else {
if (targetType instanceof PsiClassType && ((PsiClassType)targetType).isRaw()) {
setErased();
}
myConstraints.add(new TypeCompatibilityConstraint(myErased ? TypeConversionUtil.erasure(targetType) : GenericsUtil.eliminateWildcards(targetType, false), returnType));
}
registerConstraints(returnType, targetType);
}
}
}
@@ -330,11 +322,26 @@ public class InferenceSession {
for (PsiClassType thrownType : method.getThrowsList().getReferencedTypes()) {
final InferenceVariable variable = getInferenceVariable(thrownType);
if (variable != null) {
variable.setThrows();
variable.setThrownBound();
}
}
}
private void registerConstraints(PsiType returnType, PsiType targetType) {
final InferenceVariable inferenceVariable = shouldResolveAndInstantiate(returnType, targetType);
if (inferenceVariable != null) {
resolveBounds(Collections.singletonList(inferenceVariable), mySiteSubstitutor, true);
myConstraints.add(new TypeCompatibilityConstraint(inferenceVariable.getInstantiation(), returnType));
}
else {
if (targetType instanceof PsiClassType && ((PsiClassType)targetType).isRaw()) {
setErased();
}
myConstraints.add(new TypeCompatibilityConstraint(myErased ? TypeConversionUtil.erasure(targetType) : GenericsUtil.eliminateWildcards(
targetType, false), returnType));
}
}
private InferenceVariable shouldResolveAndInstantiate(PsiType returnType, PsiType targetType) {
final InferenceVariable inferenceVariable = getInferenceVariable(returnType);
if (inferenceVariable != null) {
@@ -374,7 +381,7 @@ public class InferenceSession {
return false;
}
private PsiType getTargetType(final PsiExpression context) {
private PsiType getTargetType(final PsiExpression context, PsiType returnType) {
final PsiElement parent = PsiUtil.skipParenthesizedExprUp(context.getParent());
if (parent instanceof PsiExpressionList) {
final PsiElement gParent = parent.getParent();
@@ -384,7 +391,7 @@ public class InferenceSession {
final Pair<PsiMethod, PsiSubstitutor> pair = MethodCandidateInfo.getCurrentMethod(argumentList);
final JavaResolveResult resolveResult;
if (pair == null) {
final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(gParent, context.getContainingFile()) {
final MethodCandidatesProcessor processor = new MethodResolverProcessor((PsiCallExpression)gParent, argumentList, context.getContainingFile()) {
@Override
protected PsiType[] getExpressionTypes(PsiExpressionList argumentList) {
if (argumentList != null) {
@@ -416,7 +423,13 @@ public class InferenceSession {
for (JavaResolveResult result : results) {
final PsiType type = getTypeByMethod(context, argumentList, null, result, result.getElement());
if (type != null) {
return type;
final Set<PsiTypeParameter> parameters = myInferenceVariables.keySet();
final InferenceSession session = new InferenceSession(parameters.toArray(new PsiTypeParameter[parameters.size()]), mySiteSubstitutor, myManager, myContext);
session.copy(this);
session.registerConstraints(returnType, type);
if (session.repeatInferencePhases(true)) {
return type;
}
}
}
return null;
@@ -427,7 +440,7 @@ public class InferenceSession {
} else if (parent instanceof PsiConditionalExpression) {
PsiType targetType = PsiTypesUtil.getExpectedTypeByParent((PsiExpression)parent);
if (targetType == null) {
targetType = getTargetType((PsiExpression)parent);
targetType = getTargetType((PsiExpression)parent, returnType);
}
return targetType;
}
@@ -437,6 +450,12 @@ public class InferenceSession {
return null;
}
private void copy(InferenceSession session) {
for (PsiTypeParameter parameter : session.myInferenceVariables.keySet()) {
myInferenceVariables.put(parameter, session.myInferenceVariables.get(parameter).copy());
}
}
private PsiType getTypeByMethod(PsiExpression context,
PsiExpressionList argumentList,
Pair<PsiMethod, PsiSubstitutor> pair,
@@ -83,7 +83,20 @@ public class InferenceVariable {
return myThrownBound;
}
public void setThrows() {
public void setThrownBound() {
myThrownBound = true;
}
public InferenceVariable copy() {
final InferenceVariable variable = new InferenceVariable(myParameter);
for (InferenceBound bound : InferenceBound.values()) {
for (PsiType type : getBounds(bound)) {
variable.addBound(type, bound);
}
}
if (myThrownBound) {
variable.setThrownBound();
}
return variable;
}
}
@@ -146,7 +146,7 @@ public class CheckedExceptionCompatibilityConstraint extends InputOutputConstrai
for (PsiType expectedNonProperThrownType : expectedNonProperThrownTypes) {
final InferenceVariable variable = session.getInferenceVariable(expectedNonProperThrownType);
LOG.assertTrue(variable != null);
variable.setThrows();
variable.setThrownBound();
}
}
}
@@ -27,10 +27,15 @@ import org.jetbrains.annotations.NotNull;
public class MethodResolverProcessor extends MethodCandidatesProcessor {
private boolean myStopAcceptingCandidates = false;
public MethodResolverProcessor(@NotNull PsiMethodCallExpression place, @NotNull PsiFile placeFile){
this(place, placeFile, new PsiConflictResolver[]{new JavaMethodsConflictResolver(place.getArgumentList(),
PsiUtil.getLanguageLevel(placeFile))});
setArgumentList(place.getArgumentList());
public MethodResolverProcessor(@NotNull PsiMethodCallExpression place, @NotNull PsiFile placeFile) {
this(place, place.getArgumentList(), placeFile);
}
public MethodResolverProcessor(@NotNull PsiCallExpression place,
@NotNull PsiExpressionList argumentList,
@NotNull PsiFile placeFile){
this(place, placeFile, new PsiConflictResolver[]{new JavaMethodsConflictResolver(argumentList, PsiUtil.getLanguageLevel(placeFile))});
setArgumentList(argumentList);
obtainTypeArguments(place);
}
@@ -29,7 +29,7 @@ public class ConcurrentCollectors {
static <T, K, D, M1 extends Map<K, D>> C<T, M1> groupingBy(F<M1> f,
C<T, D> c,
BiConsumer<M1, T> consumer) {
return new CImpl<>(f, consumer, <error descr="Inferred type 'M1' for type parameter 'M2' is not within its bound; should implement 'ConcurrentCollectors.ConcurrentMap<java.lang.Object,D>'">arg(c.getOp())</error>);
return new CImpl<><error descr="'CImpl(ConcurrentCollectors.F<M1>, ConcurrentCollectors.BiConsumer<M1,T>, ConcurrentCollectors.BiOp<M1>)' in 'ConcurrentCollectors.CImpl' cannot be applied to '(ConcurrentCollectors.F<M1>, ConcurrentCollectors.BiConsumer<M1,T>, ConcurrentCollectors.BiOp<ConcurrentCollectors.ConcurrentMap<java.lang.Object,D>>)'">(f, consumer, arg(c.getOp()))</error>;
}
static <K, V, M2 extends ConcurrentMap<K, V>> BiOp<M2> arg(BiOp<V> op) {
@@ -0,0 +1,72 @@
import java.util.Arrays;
import static java.lang.System.out;
public final class LambdaMain {
public static void main(final String... args) {
for (final A<String, X> a : Arrays.<A<String, X>>asList(new A<String, X>() {
@Override
public String foo(final String ignored)
throws X {
throw new X();
}
}, ignored -> { throw new X(); }))
try {
test(a, "Bob");
} catch (final X x) {
x.printStackTrace();
}
try {
out.println(test(new A<String, X>() {
@Override
public String foo(final String ignored)
throws X {
throw new X();
}
}, "Bob"));
} catch (final Exception e) {
e.printStackTrace();
}
try {
out.println(test(ignored -> { throw new X(); }, "Bob"));
} catch (final Exception e) {
e.printStackTrace();
}
}
static class X
extends Exception {}
interface A<T, E extends Exception> {
T foo(T ignored)
throws E;
}
static <T, E extends Exception> T test(final A<T, E> a, final T ignored)
throws E {
return a.foo(ignored);
}
}
final class LambdaMainTest {
public void main(A<String> a) {
println(test(a));
}
public void println(boolean x) {}
public void println(String x) {}
interface A<T> {
T foo(T ignored) ;
}
static <T> T test (final A<T> a) {
return null;
}
}
@@ -120,6 +120,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testTargetTypeByOverloadedMethod2() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(false);
}