mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
lambda.isValueCompatible should not cache inference results for calls inside lambda body as they may depend on incomplete top level inference where LambdaExpressionCompatibilityConstraint is reduced (IDEA-156311)
This commit is contained in:
@@ -887,6 +887,17 @@ public class InferenceSession {
|
||||
if (typeTypeByParentCall != null) {
|
||||
return LambdaUtil.getFunctionalInterfaceReturnType(FunctionalInterfaceParameterizationUtil.getGroundTargetType(typeTypeByParentCall, lambdaExpression));
|
||||
}
|
||||
|
||||
//during checked exception constraint processing
|
||||
//we may need to infer types for nested calls to infer unhandled exceptions inside lambda body
|
||||
//at this time, types of interface method parameter types must be already calculated
|
||||
// that's why walkUp in InferenceSessionContainer stops at this point and
|
||||
//that's why we can reuse this type here
|
||||
final PsiType cachedLambdaType = LambdaUtil.getFunctionalTypeMap().get(lambdaExpression);
|
||||
if (cachedLambdaType != null) {
|
||||
return LambdaUtil.getFunctionalInterfaceReturnType(FunctionalInterfaceParameterizationUtil.getGroundTargetType(cachedLambdaType, lambdaExpression));
|
||||
}
|
||||
|
||||
return inferParent || !(PsiUtil.skipParenthesizedExprUp(lambdaExpression.getParent()) instanceof PsiExpressionList)
|
||||
? LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression.getFunctionalInterfaceType()) : null;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.tree.java;
|
||||
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
@@ -107,6 +108,18 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
|
||||
@Override
|
||||
public boolean isValueCompatible() {
|
||||
//it could be called when functional type of lambda expression is not yet defined (during lambda expression compatibility constraint reduction)
|
||||
//thus inferred results for calls inside could be wrong and should not be cached
|
||||
final Boolean result = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(this, false, new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
return isValueCompatibleNoCache();
|
||||
}
|
||||
});
|
||||
return result != null && result;
|
||||
}
|
||||
|
||||
private boolean isValueCompatibleNoCache() {
|
||||
final PsiElement body = getBody();
|
||||
if (body instanceof PsiCodeBlock) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.io.IOException;
|
||||
|
||||
interface A {
|
||||
}
|
||||
|
||||
interface B extends A {
|
||||
}
|
||||
|
||||
interface D<TT extends Exception> {
|
||||
<T extends A, E extends TT> T foo() throws E;
|
||||
}
|
||||
|
||||
class E {
|
||||
|
||||
void bar(D<RuntimeException> d) {
|
||||
foobar(supplier(() -> {
|
||||
try {
|
||||
return d.foo();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
}));
|
||||
foobar(supplier(() -> {
|
||||
return d.foo();
|
||||
}));
|
||||
|
||||
foobar(supplier(() -> d.foo()));
|
||||
foobar(supplier(d::foo));
|
||||
|
||||
foobar(supplier(() -> {
|
||||
throw new RuntimeException();
|
||||
}));
|
||||
}
|
||||
|
||||
<T> Supplier<T> supplier(Supplier<T> s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void foobar(Supplier<B> s) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Test {
|
||||
interface A {
|
||||
}
|
||||
|
||||
interface B extends A {
|
||||
}
|
||||
|
||||
interface D {
|
||||
<T extends A> T foo() throws IOException;
|
||||
}
|
||||
|
||||
class E {
|
||||
|
||||
void bar(D d) {
|
||||
foobar(supplier(() -> {
|
||||
try {
|
||||
return d.foo();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
<T> Supplier<T> supplier(Supplier<T> s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void foobar(Supplier<B> s) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestNoTypeParameterBounds {
|
||||
|
||||
interface A {
|
||||
}
|
||||
|
||||
interface B extends A {
|
||||
}
|
||||
|
||||
interface D {
|
||||
<T extends A, E extends Throwable> T foo() throws E;
|
||||
}
|
||||
|
||||
class E {
|
||||
|
||||
void bar(D d) {
|
||||
foobar(supplier(() -> {
|
||||
try {
|
||||
return d.foo();
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
}));
|
||||
foobar(supplier(() -> {
|
||||
return d.foo();
|
||||
}));
|
||||
|
||||
foobar(supplier(() -> d.foo()));
|
||||
foobar(supplier(d::foo));
|
||||
|
||||
foobar(supplier(() -> {
|
||||
throw new RuntimeException();
|
||||
}));
|
||||
}
|
||||
|
||||
<T> Supplier<T> supplier(Supplier<T> s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void foobar(Supplier<B> s) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -324,6 +324,10 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testExceptionInLambdaBodyCheck() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user