mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
new inference: target type detection according to consequence inference (IDEA-120193)
This commit is contained in:
+36
-17
@@ -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,
|
||||
|
||||
+14
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+72
@@ -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;
|
||||
}
|
||||
}
|
||||
+4
@@ -120,6 +120,10 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testTargetTypeByOverloadedMethod2() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user