diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java index f0c07886e73d..0b2706e065cf 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java @@ -298,9 +298,9 @@ public class JavaCompletionSorting { private final CompletionParameters myParameters; private final CompletionLocation myLocation; - public PreferDefaultTypeWeigher(ExpectedTypeInfo[] expectedTypes, CompletionParameters parameters) { + public PreferDefaultTypeWeigher(@NotNull ExpectedTypeInfo[] expectedTypes, CompletionParameters parameters) { super("defaultType"); - myExpectedTypes = expectedTypes == null ? null : ContainerUtil.map2Array(expectedTypes, ExpectedTypeInfo.class, info -> { + myExpectedTypes = ContainerUtil.map2Array(expectedTypes, ExpectedTypeInfo.class, info -> { PsiType type = removeClassWildcard(info.getType()); PsiType defaultType = removeClassWildcard(info.getDefaultType()); if (type == info.getType() && defaultType == info.getDefaultType()) { @@ -328,25 +328,16 @@ public class JavaCompletionSorting { } } - if (myExpectedTypes == null) return MyResult.normal; - PsiType itemType = JavaCompletionUtil.getLookupElementType(item); - if (itemType == null || !itemType.isValid()) return MyResult.normal; - - if (object instanceof PsiClass) { - for (final ExpectedTypeInfo info : myExpectedTypes) { - if (TypeConversionUtil.erasure(info.getType().getDeepComponentType()).equals(TypeConversionUtil.erasure(itemType))) { - return AbstractExpectedTypeSkipper.skips(item, myLocation) ? MyResult.expectedNoSelect : MyResult.exactlyExpected; - } - } + if (isExactlyExpected(item, itemType)) { + return AbstractExpectedTypeSkipper.skips(item, myLocation) ? MyResult.expectedNoSelect : MyResult.exactlyExpected; } + if (itemType == null) return MyResult.normal; + for (final ExpectedTypeInfo expectedInfo : myExpectedTypes) { final PsiType defaultType = expectedInfo.getDefaultType(); final PsiType expectedType = expectedInfo.getType(); - if (!expectedType.isValid()) { - return MyResult.normal; - } if (defaultType != expectedType) { if (defaultType.equals(itemType)) { @@ -365,6 +356,27 @@ public class JavaCompletionSorting { return MyResult.normal; } + private boolean isExactlyExpected(@NotNull LookupElement item, @Nullable PsiType itemType) { + if (JavaCompletionUtil.SUPER_METHOD_PARAMETERS.get(item) != null) { + return true; + } + if (itemType == null || itemType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || hasUnboundTypeArguments(item)) { + return false; + } + + return ContainerUtil.exists(myExpectedTypes, info -> box(info.getType().getDeepComponentType()).equals(box(itemType))); + } + + private static boolean hasUnboundTypeArguments(@NotNull LookupElement item) { + JavaMethodCallElement call = item.as(JavaMethodCallElement.CLASS_CONDITION_KEY); + return call != null && !call.getInferenceSubstitutor().equals(PsiSubstitutor.EMPTY); + } + + private PsiType box(PsiType expectedType) { + PsiClassType boxed = expectedType instanceof PsiPrimitiveType ? ((PsiPrimitiveType)expectedType).getBoxedType(myParameters.getPosition()) : null; + return boxed != null ? boxed : expectedType; + } + private static PsiType removeClassWildcard(PsiType type) { if (type instanceof PsiClassType) { final PsiClass psiClass = ((PsiClassType)type).resolve(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodMergingContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodMergingContributor.java index 87d8ee3830a8..c443cae95a99 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodMergingContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaMethodMergingContributor.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,15 +53,13 @@ public class JavaMethodMergingContributor extends CompletionContributor { return AutoCompletionDecision.SHOW_LOOKUP; } - final PsiMethod method = (PsiMethod)o; - final JavaChainLookupElement chain = item.as(JavaChainLookupElement.CLASS_CONDITION_KEY); - final String name = method.getName() + "#" + (chain == null ? "" : chain.getQualifier().getLookupString()); + String name = joinLookupStrings(item); if (commonName != null && !commonName.equals(name)) { return AutoCompletionDecision.SHOW_LOOKUP; } commonName = name; - allMethods.add(method); + allMethods.add((PsiMethod)o); } for (LookupElement item : items) { @@ -73,6 +72,10 @@ public class JavaMethodMergingContributor extends CompletionContributor { return super.handleAutoCompletionPossibility(context); } + public static String joinLookupStrings(LookupElement item) { + return StreamEx.of(item.getAllLookupStrings()).sorted().joining("#"); + } + public static LookupElement findBestOverload(LookupElement[] items) { LookupElement best = items[0]; for (int i = 1; i < items.length; i++) { diff --git a/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferGlobalMembersReturningExpectedType.java b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferGlobalMembersReturningExpectedType.java new file mode 100644 index 000000000000..6a8cd70dfadc --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferGlobalMembersReturningExpectedType.java @@ -0,0 +1,12 @@ +class Map { + static class Builder {} + static Builder builder() {} +} +class BiMap extends Map { + static class Builder extends Map.Builder {} + static Builder builder() {} +} + +class Usage { + Map.Builder b = bui +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy index 416b6f471429..a485ddb15f34 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy @@ -33,7 +33,7 @@ class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase { } void testJComponentAdd() throws Throwable { - checkPreferredItems(0, "name", "b", "fooBean239", "foo", "this") + checkPreferredItems(0, "name", "b", "fooBean239", "foo") } void testJComponentAddNew() throws Throwable { @@ -323,7 +323,7 @@ class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase { } void testPreferLocalOverThis() { - checkPreferredItems 0, 'value', 'this', 'hashCode' + checkPreferredItems 0, 'value', 'hashCode', 'this' } void testGetLogger() { @@ -339,7 +339,7 @@ class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase { } void testPreferLocalWildcardClassOverObject() { - checkPreferredItems 0, 'type', 'Object.class' + checkPreferredItems 0, 'type', 'forName', 'forName', 'Object.class' } void testPreferStringsInStringConcatenation() { @@ -361,6 +361,13 @@ class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase { assert lookup.items.size() == 2 } + void testPreferGlobalMembersReturningExpectedType() { + configureNoCompletion(getTestName(false) + ".java") + def items = myFixture.complete(CompletionType.SMART, 2) + assert LookupElementPresentation.renderElement(items[0]).itemText == 'Map.builder' + assert LookupElementPresentation.renderElement(items[1]).itemText == 'BiMap.builder' + } + @Override protected String getBasePath() { return JavaTestUtil.getRelativeJavaTestDataPath() + BASE_PATH diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index e04f6118631a..1f62b450f756 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -830,7 +830,7 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { } public void testNoClassLiteral() throws Exception { doActionTest(); - assertStringItems("Object.class", "getClass", "forName", "forName"); + assertStringItems("forName", "forName", "Object.class", "getClass"); } public void testClassLiteralInAnno2() throws Throwable { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMethodMergingContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMethodMergingContributor.java index 45d0baf3da19..08e754b1e7e4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMethodMergingContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GrMethodMergingContributor.java @@ -51,9 +51,8 @@ public class GrMethodMergingContributor extends CompletionContributor { return AutoCompletionDecision.SHOW_LOOKUP; } - final PsiMethod method = (PsiMethod)o; - final JavaChainLookupElement chain = item.as(JavaChainLookupElement.CLASS_CONDITION_KEY); - final String name = method.getName() + "#" + (chain == null ? "" : chain.getQualifier().getLookupString()); + PsiMethod method = (PsiMethod)o; + String name = JavaMethodMergingContributor.joinLookupStrings(item); if (commonName != null && !commonName.equals(name)) { return AutoCompletionDecision.SHOW_LOOKUP;