diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 49e89479f19c..5b0056898c48 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -254,7 +254,40 @@ public class JavaCompletionUtil { return subst.get().substitute(rawType); } - public static Set processJavaReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter, + public static Set processJavaReference(final PsiElement element, + final PsiJavaReference javaReference, + final ElementFilter elementFilter, + final JavaCompletionProcessor.Options options, + final PrefixMatcher matcher, + final CompletionParameters parameters) { + PsiElement elementParent = element.getContext(); + if (elementParent instanceof PsiReferenceExpression) { + final PsiExpression qualifierExpression = ((PsiReferenceExpression)elementParent).getQualifierExpression(); + if (qualifierExpression instanceof PsiReferenceExpression) { + final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve(); + if (resolve instanceof PsiParameter) { + final PsiElement declarationScope = ((PsiParameter)resolve).getDeclarationScope(); + if (((PsiParameter)resolve).getType() instanceof PsiLambdaParameterType) { + final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)declarationScope; + final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex((PsiParameter)resolve); + final Set set = new LinkedHashSet(); + final boolean overloadsFound = LambdaUtil.processParentOverloads(lambdaExpression, new Consumer() { + @Override + public void consume(PsiType functionalInterfaceType) { + PsiType qualifierType = LambdaUtil.getLambdaParameterFromType(functionalInterfaceType, parameterIndex); + PsiReferenceExpression fakeRef = createReference("xxx.xxx", createContextWithXxxVariable(element, qualifierType)); + set.addAll(processJavaQualifiedReference(fakeRef.getReferenceNameElement(), fakeRef, elementFilter, options, matcher, parameters)); + } + }); + if (overloadsFound) return set; + } + } + } + } + return processJavaQualifiedReference(element, javaReference, elementFilter, options, matcher, parameters); + } + + private static Set processJavaQualifiedReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter, JavaCompletionProcessor.Options options, final PrefixMatcher matcher, CompletionParameters parameters) { final Set set = new LinkedHashSet(); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 5872b4f76d96..e6b846f33644 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.util.*; +import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.Contract; @@ -352,21 +353,7 @@ public class LambdaUtil { } } final JavaResolveResult resolveResult = properties != null ? properties.getInfo() : contextCall.resolveMethodGenerics(); - final PsiElement resolve = resolveResult.getElement(); - if (resolve instanceof PsiMethod) { - final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters(); - final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters); - if (finalLambdaIdx < parameters.length) { - if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]); - return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, !MethodCandidateInfo.isOverloadCheck(), new Computable() { - @Override - public PsiType compute() { - return resolveResult.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx])); - } - }); - } - } - return null; + return getSubstitutedType(expression, tryToSubstitute, lambdaIdx, resolveResult); } } } @@ -384,6 +371,66 @@ public class LambdaUtil { return null; } + @Nullable + private static PsiType getSubstitutedType(PsiElement expression, + boolean tryToSubstitute, + int lambdaIdx, + final JavaResolveResult resolveResult) { + final PsiElement resolve = resolveResult.getElement(); + if (resolve instanceof PsiMethod) { + final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters(); + final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod)resolve, parameters); + if (finalLambdaIdx < parameters.length) { + if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]); + return PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, !MethodCandidateInfo.isOverloadCheck(), new Computable() { + @Override + public PsiType compute() { + return resolveResult.getSubstitutor().substitute(getNormalizedType(parameters[finalLambdaIdx])); + } + }); + } + } + return null; + } + + public static boolean processParentOverloads(PsiFunctionalExpression functionalExpression, final Consumer overloadProcessor) { + LOG.assertTrue(PsiTypesUtil.getExpectedTypeByParent(functionalExpression) == null); + PsiElement parent = functionalExpression.getParent(); + PsiElement expr = functionalExpression; + while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) { + if (parent instanceof PsiConditionalExpression && + ((PsiConditionalExpression)parent).getThenExpression() != expr && + ((PsiConditionalExpression)parent).getElseExpression() != expr) break; + expr = parent; + parent = parent.getParent(); + } + if (parent instanceof PsiExpressionList) { + final PsiExpressionList expressionList = (PsiExpressionList)parent; + final int lambdaIdx = getLambdaIdx(expressionList, functionalExpression); + if (lambdaIdx > -1) { + + PsiElement gParent = expressionList.getParent(); + + if (gParent instanceof PsiAnonymousClass) { + gParent = gParent.getParent(); + } + + if (gParent instanceof PsiMethodCallExpression) { + final Set types = new HashSet(); + final JavaResolveResult[] results = ((PsiMethodCallExpression)gParent).getMethodExpression().multiResolve(true); + for (JavaResolveResult result : results) { + final PsiType functionalExpressionType = getSubstitutedType(functionalExpression, true, lambdaIdx, result); + if (functionalExpressionType != null && types.add(functionalExpressionType)) { + overloadProcessor.consume(functionalExpressionType); + } + } + return true; + } + } + } + return false; + } + @Nullable private static PsiType extractFunctionalConjunct(PsiIntersectionType type) { PsiType conjunct = null; @@ -605,6 +652,21 @@ public class LambdaUtil { return null; } + @Nullable + public static PsiType getLambdaParameterFromType(PsiType functionalInterfaceType, int parameterIndex) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType); + if (resolveResult != null) { + final PsiMethod method = getFunctionalInterfaceMethod(functionalInterfaceType); + if (method != null) { + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameterIndex < parameters.length) { + return getSubstitutor(method, resolveResult).substitute(parameters[parameterIndex].getType()); + } + } + } + return null; + } + public static class TypeParamsChecker extends PsiTypeVisitor { private PsiMethod myMethod; private final PsiClass myClass; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java index 9c7cee0661e8..08018fa46594 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java @@ -33,7 +33,6 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; import com.intellij.reference.SoftReference; import com.intellij.ui.RowIcon; import com.intellij.util.IncorrectOperationException; @@ -73,13 +72,13 @@ public class PsiParameterImpl extends JavaStubPsiElement imple if (type instanceof PsiIntersectionType) { final PsiType[] conjuncts = ((PsiIntersectionType)type).getConjuncts(); for (PsiType conjunct : conjuncts) { - final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, conjunct); + final PsiType lambdaParameterFromType = LambdaUtil.getLambdaParameterFromType(conjunct, parameterIndex); if (lambdaParameterFromType != null) { return lambdaParameterFromType; } } } else { - final PsiType lambdaParameterFromType = getLambdaParameterFromType(parameterIndex, type); + final PsiType lambdaParameterFromType = LambdaUtil.getLambdaParameterFromType(type, parameterIndex); if (lambdaParameterFromType != null) { return lambdaParameterFromType; } @@ -90,20 +89,6 @@ public class PsiParameterImpl extends JavaStubPsiElement imple return new PsiLambdaParameterType(param); } - private static PsiType getLambdaParameterFromType(int parameterIndex, PsiType conjunct) { - final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(conjunct); - if (resolveResult != null) { - final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(conjunct); - if (method != null) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (parameterIndex < parameters.length) { - return LambdaUtil.getSubstitutor(method, resolveResult).substitute(parameters[parameterIndex].getType()); - } - } - } - return null; - } - @Override public void subtreeChanged() { super.subtreeChanged(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/LambdaInAmbiguousCall.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/LambdaInAmbiguousCall.java new file mode 100644 index 000000000000..5f779b333fef --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/completion/normal/LambdaInAmbiguousCall.java @@ -0,0 +1,7 @@ +import java.util.stream.*; + +class Foo { + { + Collectors.toMap(l -> l.t) + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Normal8CompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Normal8CompletionTest.groovy index b7085e17fe58..8fde79dbd6de 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Normal8CompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Normal8CompletionTest.groovy @@ -228,4 +228,9 @@ class Test88 { selectItem(myItems.find { it.lookupString.contains('empty') }) checkResultByFile(getTestName(false) + "_after.java") } + + public void testLambdaInAmbiguousCall() { + configureByTestName() + myFixture.assertPreferredCompletionItems(0, 'toString', 'wait') + } }