inference: skip lambda content and all its dependants when lambda type parameter type is calculated

This commit is contained in:
Anna Kozlova
2016-03-03 11:39:53 +01:00
parent 953c6249c4
commit 1e764c9961
16 changed files with 167 additions and 72 deletions
@@ -17,6 +17,8 @@ package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -35,6 +37,7 @@ import java.util.*;
* Date: 7/17/12
*/
public class LambdaUtil {
public static final RecursionGuard ourParameterGuard = RecursionManager.createGuard("lambdaParameterGuard");
public static ThreadLocal<Map<PsiElement, PsiType>> ourFunctionTypes = new ThreadLocal<Map<PsiElement, PsiType>>();
private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
@@ -667,6 +670,10 @@ public class LambdaUtil {
return null;
}
public static boolean isLambdaParameterCheck() {
return !ourParameterGuard.currentStack().isEmpty();
}
public static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
@@ -294,7 +294,9 @@ public class MethodCandidateInfo extends CandidateInfo{
if (!stackStamp.mayCacheNow() ||
isOverloadCheck() ||
!includeReturnConstraint && myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) ||
getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent())) {
getMarkerList() != null && PsiResolveHelper.ourGraphGuard.currentStack().contains(getMarkerList().getParent()) ||
LambdaUtil.isLambdaParameterCheck()
) {
return inferredSubstitutor;
}
@@ -435,7 +435,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType {
public PsiType[] getArgumentTypes() {
if (myExpressionTypes == null) {
final PsiType[] expressionTypes = argumentList.getExpressionTypes();
if (MethodCandidateInfo.isOverloadCheck()) {
if (MethodCandidateInfo.isOverloadCheck() || LambdaUtil.isLambdaParameterCheck()) {
return expressionTypes;
}
myExpressionTypes = expressionTypes;
@@ -19,6 +19,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.impl.CheckUtil;
import com.intellij.psi.impl.ElementPresentationUtil;
@@ -67,8 +68,14 @@ public class PsiParameterImpl extends JavaStubPsiElement<PsiParameterStub> imple
if (parameterIndex > -1) {
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class);
if (lambdaExpression != null) {
PsiType type = FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(lambdaExpression, true), lambdaExpression);
final PsiType functionalInterfaceType = LambdaUtil.ourParameterGuard.doPreventingRecursion(param, false,
new Computable<PsiType>() {
@Override
public PsiType compute() {
return LambdaUtil.getFunctionalInterfaceType(lambdaExpression, true);
}
});
PsiType type = FunctionalInterfaceParameterizationUtil.getGroundTargetType(functionalInterfaceType, lambdaExpression);
if (type instanceof PsiIntersectionType) {
final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts();
for (PsiType conjunct : conjuncts) {
@@ -90,7 +90,7 @@ public class JavaResolveCache {
}
//cache standalone expression types as they do not depend on the context
final boolean isOverloadCheck = MethodCandidateInfo.isOverloadCheck();
final boolean isOverloadCheck = MethodCandidateInfo.isOverloadCheck() || LambdaUtil.isLambdaParameterCheck();
if (isOverloadCheck && PsiPolyExpressionUtil.isPolyExpression(expr)) {
return type;
}
@@ -361,11 +361,13 @@ public class InferenceSession {
if (parameters != null && args != null && !isOverloadCheck()) {
final Set<ConstraintFormula> additionalConstraints = new LinkedHashSet<ConstraintFormula>();
final HashSet<ConstraintFormula> ignoredConstraints = new HashSet<ConstraintFormula>();
if (parameters.length > 0) {
collectAdditionalConstraints(parameters, args, properties.getMethod(), mySiteSubstitutor, additionalConstraints, properties.isVarargs(), initialSubstitutor);
collectAdditionalConstraints(parameters, args, properties.getMethod(), mySiteSubstitutor, additionalConstraints,
ignoredConstraints, properties.isVarargs(), initialSubstitutor);
}
if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) {
if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints, ignoredConstraints)) {
return;
}
}
@@ -416,21 +418,29 @@ public class InferenceSession {
PsiMethod parentMethod,
PsiSubstitutor siteSubstitutor,
Set<ConstraintFormula> additionalConstraints,
Set<ConstraintFormula> ignoredConstraints,
boolean varargs,
PsiSubstitutor initialSubstitutor) {
for (int i = 0; i < args.length; i++) {
final PsiExpression arg = PsiUtil.skipParenthesizedExprDown(args[i]);
if (arg != null) {
if (MethodCandidateInfo.isOverloadCheck() && arg instanceof PsiLambdaExpression) {
for (Object expr : MethodCandidateInfo.ourOverloadGuard.currentStack()) {
if (PsiTreeUtil.getParentOfType((PsiElement)expr, PsiLambdaExpression.class) == arg) {
return;
}
}
}
final PsiSubstitutor nestedSubstitutor = myInferenceSessionContainer.findNestedSubstitutor(arg, myInferenceSubstitution);
final PsiType parameterType = nestedSubstitutor.substitute(getParameterType(parameters, i, siteSubstitutor, varargs));
if (!isPertinentToApplicability(arg, parentMethod)) {
if (arg instanceof PsiLambdaExpression) {
for (Object expr : MethodCandidateInfo.ourOverloadGuard.currentStack()) {
if (PsiTreeUtil.getParentOfType((PsiElement)expr, PsiLambdaExpression.class) == arg) {
return;
}
}
for (Object expr : LambdaUtil.ourParameterGuard.currentStack()) {
if (expr instanceof PsiParameter && ((PsiParameter)expr).getDeclarationScope() == arg) {
ignoredConstraints.add(new ExpressionCompatibilityConstraint(arg, parameterType));
return;
}
}
}
additionalConstraints.add(new ExpressionCompatibilityConstraint(arg, parameterType));
}
additionalConstraints.add(new CheckedExceptionCompatibilityConstraint(arg, parameterType));
@@ -439,12 +449,12 @@ public class InferenceSession {
//the set contains all constraint formulas that would appear in the set C when determining the poly expression's invocation type.
final PsiMethod calledMethod = getCalledMethod((PsiCall)arg);
if (calledMethod != null && PsiPolyExpressionUtil.isMethodCallPolyExpression(arg, calledMethod)) {
collectAdditionalConstraints(additionalConstraints, (PsiCall)arg, initialSubstitutor);
collectAdditionalConstraints(additionalConstraints, ignoredConstraints, (PsiCall)arg, initialSubstitutor);
}
}
else if (arg instanceof PsiLambdaExpression &&
isPertinentToApplicability(arg, parentMethod)) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)arg, parameterType,
collectLambdaReturnExpression(additionalConstraints, ignoredConstraints, (PsiLambdaExpression)arg, parameterType,
!isProperType(initialSubstitutor.substitute(parameterType)),
initialSubstitutor);
}
@@ -472,19 +482,22 @@ public class InferenceSession {
}
private void collectLambdaReturnExpression(Set<ConstraintFormula> additionalConstraints,
Set<ConstraintFormula> ignoredConstraints,
PsiLambdaExpression lambdaExpression,
PsiType parameterType,
boolean addConstraint, PsiSubstitutor initialSubstitutor) {
boolean addConstraint,
PsiSubstitutor initialSubstitutor) {
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(parameterType);
if (interfaceReturnType != null) {
final List<PsiExpression> returnExpressions = LambdaUtil.getReturnExpressions(lambdaExpression);
for (PsiExpression returnExpression : returnExpressions) {
processReturnExpression(additionalConstraints, returnExpression, interfaceReturnType, addConstraint, initialSubstitutor);
processReturnExpression(additionalConstraints, ignoredConstraints, returnExpression, interfaceReturnType, addConstraint, initialSubstitutor);
}
}
}
private void processReturnExpression(Set<ConstraintFormula> additionalConstraints,
Set<ConstraintFormula> ignoredConstraints,
PsiExpression returnExpression,
PsiType functionalType,
boolean addConstraint,
@@ -493,7 +506,7 @@ public class InferenceSession {
if (addConstraint) {
final PsiMethod calledMethod = getCalledMethod((PsiCallExpression)returnExpression);
if (calledMethod != null && PsiPolyExpressionUtil.isMethodCallPolyExpression(returnExpression, calledMethod)) {
collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)returnExpression, initialSubstitutor);
collectAdditionalConstraints(additionalConstraints, ignoredConstraints, (PsiCallExpression)returnExpression, initialSubstitutor);
}
}
else {
@@ -501,19 +514,20 @@ public class InferenceSession {
}
}
else if (returnExpression instanceof PsiParenthesizedExpression) {
processReturnExpression(additionalConstraints, ((PsiParenthesizedExpression)returnExpression).getExpression(), functionalType, addConstraint, initialSubstitutor);
processReturnExpression(additionalConstraints, ignoredConstraints, ((PsiParenthesizedExpression)returnExpression).getExpression(), functionalType, addConstraint, initialSubstitutor);
}
else if (returnExpression instanceof PsiConditionalExpression) {
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType, addConstraint, initialSubstitutor);
processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType, addConstraint, initialSubstitutor);
processReturnExpression(additionalConstraints, ignoredConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType, addConstraint, initialSubstitutor);
processReturnExpression(additionalConstraints, ignoredConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType, addConstraint, initialSubstitutor);
}
else if (returnExpression instanceof PsiLambdaExpression) {
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)returnExpression, functionalType, myErased, initialSubstitutor);
collectLambdaReturnExpression(additionalConstraints, ignoredConstraints, (PsiLambdaExpression)returnExpression, functionalType, myErased, initialSubstitutor);
}
}
private void collectAdditionalConstraints(final Set<ConstraintFormula> additionalConstraints,
final PsiCall callExpression,
final Set<ConstraintFormula> ignoredConstraints,
final PsiCall callExpression,
PsiSubstitutor initialSubstitutor) {
PsiExpressionList argumentList = callExpression.getArgumentList();
if (argumentList != null) {
@@ -524,7 +538,8 @@ public class InferenceSession {
final PsiExpression[] newArgs = argumentList.getExpressions();
final PsiParameter[] newParams = method.getParameterList().getParameters();
if (newParams.length > 0) {
collectAdditionalConstraints(newParams, newArgs, method, chooseSiteSubstitutor(properties, result, method), additionalConstraints, chooseVarargsMode(properties, result), initialSubstitutor);
collectAdditionalConstraints(newParams, newArgs, method, chooseSiteSubstitutor(properties, result, method), additionalConstraints,
ignoredConstraints, chooseVarargsMode(properties, result), initialSubstitutor);
}
}
}
@@ -1290,7 +1305,7 @@ public class InferenceSession {
}
}
private boolean proceedWithAdditionalConstraints(Set<ConstraintFormula> additionalConstraints) {
private boolean proceedWithAdditionalConstraints(Set<ConstraintFormula> additionalConstraints, Set<ConstraintFormula> ignoredConstraints) {
//empty substitutor should be used to resolve input variables:
//all types in additional constraints are already substituted during collecting phase,
//recursive site substitutors (T -> List<T>) would make additional constraints work with multiple times substituted types, which is incorrect.
@@ -1300,7 +1315,7 @@ public class InferenceSession {
while (!additionalConstraints.isEmpty()) {
//extract subset of constraints
final Set<ConstraintFormula> subset = buildSubset(additionalConstraints);
final Set<ConstraintFormula> subset = buildSubset(additionalConstraints, ignoredConstraints);
//collect all input variables of selection
final Set<InferenceVariable> varsToResolve = new LinkedHashSet<InferenceVariable>();
@@ -1312,7 +1327,7 @@ public class InferenceSession {
final PsiSubstitutor substitutor = resolveSubsetOrdered(varsToResolve, siteSubstitutor);
for (ConstraintFormula formula : subset) {
if (!processOneConstraint(formula, additionalConstraints, substitutor)) return false;
if (!processOneConstraint(formula, additionalConstraints, substitutor, ignoredConstraints)) return false;
}
}
return true;
@@ -1329,8 +1344,9 @@ public class InferenceSession {
}
private boolean processOneConstraint(ConstraintFormula formula,
Set<ConstraintFormula> additionalConstraints,
PsiSubstitutor substitutor) {
Set<ConstraintFormula> additionalConstraints,
PsiSubstitutor substitutor,
Set<ConstraintFormula> ignoredConstraints) {
if (myContext instanceof PsiCall) {
PsiExpressionList argumentList = ((PsiCall)myContext).getArgumentList();
@@ -1348,27 +1364,20 @@ public class InferenceSession {
if (formula instanceof ExpressionCompatibilityConstraint) {
PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression();
if (expression instanceof PsiLambdaExpression) {
PsiType parameterType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(((ExpressionCompatibilityConstraint)formula).getT(),
(PsiLambdaExpression)expression);
collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)expression, parameterType, !isProperType(parameterType), substitutor);
PsiType parameterType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(((ExpressionCompatibilityConstraint)formula).getT(), (PsiLambdaExpression)expression);
collectLambdaReturnExpression(additionalConstraints, ignoredConstraints, (PsiLambdaExpression)expression, parameterType, !isProperType(parameterType), substitutor);
}
}
return true;
}
private Set<ConstraintFormula> buildSubset(final Set<ConstraintFormula> additionalConstraints) {
private Set<ConstraintFormula> buildSubset(final Set<ConstraintFormula> additionalConstraints,
final Set<ConstraintFormula> ignoredConstraints) {
final Set<InferenceVariable> outputVariables = getOutputVariables(additionalConstraints);
final Set<InferenceVariable> ignoredOutputVariables = getOutputVariables(ignoredConstraints);
Set<ConstraintFormula> subset = new LinkedHashSet<ConstraintFormula>();
final Set<InferenceVariable> outputVariables = new HashSet<InferenceVariable>();
for (ConstraintFormula constraint : additionalConstraints) {
if (constraint instanceof InputOutputConstraintFormula) {
final Set<InferenceVariable> inputVariables = ((InputOutputConstraintFormula)constraint).getInputVariables(this);
final Set<InferenceVariable> outputVars = ((InputOutputConstraintFormula)constraint).getOutputVariables(inputVariables, this);
if (outputVars != null) {
outputVariables.addAll(outputVars);
}
}
}
Set<ConstraintFormula> noInputVariables = new LinkedHashSet<ConstraintFormula>();
for (ConstraintFormula constraint : additionalConstraints) {
@@ -1382,11 +1391,13 @@ public class InferenceSession {
final Set<InferenceVariable> dependencies = inputVariable.getDependencies(this);
dependencies.add(inputVariable);
if (!hasCapture(inputVariable)) {
for (InferenceVariable outputVariable : outputVariables) {
if (ContainerUtil.intersects(outputVariable.getDependencies(this), dependencies)) {
dependsOnOutput = true;
break;
}
if (dependsOnOutput(ignoredOutputVariables, dependencies)) {
dependsOnOutput = true;
ignoredConstraints.add(constraint);
break;
}
else {
dependsOnOutput = dependsOnOutput(outputVariables, dependencies);
}
}
@@ -1414,7 +1425,10 @@ public class InferenceSession {
}
}
if (subset.isEmpty()) {
subset.add(additionalConstraints.iterator().next()); //todo choose one constraint
additionalConstraints.removeAll(ignoredConstraints);
if (!additionalConstraints.isEmpty()) {
subset.add(additionalConstraints.iterator().next()); //todo choose one constraint
}
}
if (!noInputVariables.isEmpty()) {
@@ -1425,6 +1439,30 @@ public class InferenceSession {
return subset;
}
private boolean dependsOnOutput(Set<InferenceVariable> outputVariables, Set<InferenceVariable> dependencies) {
for (InferenceVariable outputVariable : outputVariables) {
if (ContainerUtil.intersects(outputVariable.getDependencies(this), dependencies)) {
return true;
}
}
return false;
}
@NotNull
private Set<InferenceVariable> getOutputVariables(Set<ConstraintFormula> constraintFormulas) {
final Set<InferenceVariable> outputVariables = new HashSet<InferenceVariable>();
for (ConstraintFormula constraint : constraintFormulas) {
if (constraint instanceof InputOutputConstraintFormula) {
final Set<InferenceVariable> inputVariables = ((InputOutputConstraintFormula)constraint).getInputVariables(this);
final Set<InferenceVariable> outputVars = ((InputOutputConstraintFormula)constraint).getOutputVariables(inputVariables, this);
if (outputVars != null) {
outputVariables.addAll(outputVars);
}
}
}
return outputVariables;
}
public PsiSubstitutor collectApplicabilityConstraints(final PsiMethodReferenceExpression reference,
final MethodCandidateInfo candidateInfo,
final PsiType functionalInterfaceType) {
@@ -81,7 +81,7 @@ public class InferenceSessionContainer {
if (topLevelCall != null) {
final InferenceSession session;
if (MethodCandidateInfo.isOverloadCheck() || !PsiDiamondType.ourDiamondGuard.currentStack().isEmpty()) {
if (MethodCandidateInfo.isOverloadCheck() || !PsiDiamondType.ourDiamondGuard.currentStack().isEmpty() || LambdaUtil.isLambdaParameterCheck()) {
session = startTopLevelInference(topLevelCall);
}
else {
@@ -134,9 +134,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (method != null && !method.isConstructor()) {
returnType = method.getReturnType();
if (returnType != null) {
typeParams = method.getTypeParameters();
}
typeParams = method.getTypeParameters();
}
else if (resolveResult != null) {
final PsiClass psiClass = method != null ? method.getContainingClass() : (PsiClass)resolveResult.getElement();
@@ -162,7 +160,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
return callSession;
}
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), targetType);
if (returnType != null) {
callSession.registerReturnTypeConstraints(siteSubstitutor.substitute(returnType), targetType);
}
if (callSession.repeatInferencePhases()) {
return callSession;
}
@@ -175,8 +175,8 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
session.registerIncompatibleErrorMessage(message);
}
}
return null;
}
return null;
}
return session;
}
@@ -80,7 +80,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
myArgumentsList.getText() + "; " +
"file=" + (method == null ? "<unknown>" : method.getContainingFile()));
}
return MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(myArgumentsList, true, new Computable<CandidateInfo>() {
return MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(myArgumentsList, false, new Computable<CandidateInfo>() {
@Override
public CandidateInfo compute() {
return guardedOverloadResolution(conflicts);
@@ -104,7 +104,7 @@ public class MethodCandidatesProcessor extends MethodsProcessor{
public PsiType[] getArgumentTypes() {
if (myExpressionTypes == null && argumentList != null) {
final PsiType[] expressionTypes = getExpressionTypes(argumentList);
if (MethodCandidateInfo.isOverloadCheck()) {
if (MethodCandidateInfo.isOverloadCheck() || LambdaUtil.isLambdaParameterCheck()) {
return expressionTypes;
}
myExpressionTypes = expressionTypes;
@@ -0,0 +1,14 @@
import java.util.function.Function;
import java.util.stream.Stream;
class Test {
<K> void foo(Function<String, K> f1) {}
<T> Stream<T> bar(T ts) {
return null;
}
void f(){
foo(y -> bar(y.to<caret>String()).map(a -> a.length()));
}
}
@@ -0,0 +1,10 @@
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
class StreamMainSimplified {
public static void main(Optional<Map.Entry<Integer, String>> first) {
String s1 = first.map((e) -> e.getV<caret>alue()).orElse("").substring(9);
}
}
@@ -3,8 +3,8 @@ import java.util.*;
class Main {
void foo(List<Integer> list) {
bar(list, i -> i.intValue(), i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
bar1(list, i -> i.intValue(), i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
bar(list, i -> <error descr="Bad return type in lambda expression: int cannot be converted to S_OUT">i.intValue()</error>, i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
bar1(list, i -> <error descr="Bad return type in lambda expression: int cannot be converted to S_OUT">i.intValue()</error>, i -> i.<error descr="Cannot resolve method 'unknown()'">unknown</error>());
}
<U, S_IN, S_OUT, R> R bar(List<S_IN> list,
@@ -36,3 +36,12 @@ final class Collectors {
downstream.characteristics().toArray(new Collector.Characteristics[downstream.characteristics().size()]));
}
}
class SimplifiedTest {
public static void a(Stream<Integer> classifier) {
flatMapping(classifier.map(k -> new AbstractMap.SimpleEntry<Integer, String>(k,k.toString())), Map.Entry::getKey);
}
public static <U, R> void flatMapping(Stream<U> stream, Function<U, R> downstream) {}
}
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.IdeaTestUtil;
import org.jetbrains.annotations.NonNls;
@@ -419,18 +420,17 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
}
}
assertTrue(tooltips.contains("<html><body><table border=0><tr><td>" +
"<b>identity(&nbsp;)&nbsp;</b></td><td colspan=1>in <b>Function</b>&nbsp;cannot be applied</td></tr><tr><td>to</td><td><b>()</b>&nbsp;" +
"</td></tr></table><br/>" +
"reason: no instance(s) of type variable(s) K, U exist so that Map&lt;K, U&gt; conforms to Function&lt;U, V&gt;" +
"</body></html>"));
assertTrue(tooltips.contains(
"<html><body><table border=0><tr><td>" +
"<b>identity(&nbsp;)&nbsp;</b></td><td colspan=1>in <b>Function</b>&nbsp;cannot be applied</td></tr><tr><td>to</td><td><b>()</b>&nbsp;" +
"</td></tr></table><br/>" +
"reason: no instance(s) of type variable(s) K, U exist so that Map&lt;K, U&gt; conforms to Function&lt;U, V&gt;" +
"</body></html>"));
boolean found = false;
for (String tooltip : tooltips) {
if (tooltip.contains("reason: no instance(s) of type variable(s) K, U exist so that Map&lt;K, U&gt; conforms to Function&lt;U, V&gt;")) {
found = true;
break;
}
}
if (!found) {
fail(StringUtil.join(tooltips, ", "));
}
}
private void doTest() throws Exception {
@@ -63,6 +63,14 @@ public class Java8ExpressionsCheckTest extends LightDaemonAnalyzerTestCase {
doTestCachedUnresolved();
}
public void testCacheUnresolvedMethods4() throws Exception {
doTestCachedUnresolved();
}
public void testCacheUnresolvedMethods5() throws Exception {
doTestCachedUnresolved();
}
public void testMethodOverloadsInsideLambdaHierarchy() throws Exception {
doTestAllMethodCallExpressions();
}