From 0e351d518b66d3a34b20ac189142014469702744 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Jul 2017 18:49:25 +0200 Subject: [PATCH] IDEA-175517 Generic Map Autocomplete Always uses "Object" as type instead of the actual type --- .../intellij/codeInsight/CodeInsightUtil.java | 102 ++++++++++-------- .../TypeArgumentCompletionProvider.java | 48 +-------- .../NewHashMapTypeArguments-out.java | 5 + .../smartType/NewHashMapTypeArguments.java | 5 + .../smartType/NewMapTypeArguments-out.java | 5 + .../smartType/NewMapTypeArguments.java | 5 + .../completion/SmartType18CompletionTest.java | 3 + .../completion/SmartTypeCompletionTest.java | 3 + 8 files changed, 85 insertions(+), 91 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments.java diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index 59b820e44cbd..02a8f7a44b62 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -15,10 +15,7 @@ */ package com.intellij.codeInsight; -import com.intellij.codeInsight.completion.AllClassesGetter; -import com.intellij.codeInsight.completion.CompletionUtil; -import com.intellij.codeInsight.completion.JavaCompletionUtil; -import com.intellij.codeInsight.completion.PrefixMatcher; +import com.intellij.codeInsight.completion.*; import com.intellij.lang.Language; import com.intellij.lang.StdLanguages; import com.intellij.openapi.diagnostic.Logger; @@ -340,11 +337,12 @@ public class CodeInsightUtil { PsiManager manager = context.getManager(); JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject()); PsiResolveHelper resolveHelper = facade.getResolveHelper(); + PsiElementFactory factory = facade.getElementFactory(); return inheritor -> { ProgressManager.checkCanceled(); - if (!facade.getResolveHelper().isAccessible(inheritor, context, null)) { + if (!resolveHelper.isAccessible(inheritor, context, null)) { return true; } @@ -357,57 +355,71 @@ public class CodeInsightUtil { PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY); if (superSubstitutor == null) return true; - if (getRawSubtypes) { - result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); - return true; - } - PsiSubstitutor inheritorSubstitutor = PsiSubstitutor.EMPTY; - for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) { - for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) { - final PsiType substituted = superSubstitutor.substitute(baseParameter); - PsiClass inheritorCandidateParameter = PsiUtil.resolveClassInType(substituted); - if (inheritorCandidateParameter instanceof PsiTypeParameter && - ((PsiTypeParameter)inheritorCandidateParameter).getOwner() == inheritor && - inheritorCandidateParameter != inheritorParameter) { - continue; - } - PsiType arg = baseSubstitutor.substitute(baseParameter); - if (arg instanceof PsiWildcardType) { - PsiType bound = ((PsiWildcardType)arg).getBound(); - arg = bound != null ? bound : ((PsiWildcardType)arg).getExtendsBound(); - } - PsiType substitution = resolveHelper.getSubstitutionForTypeParameter(inheritorParameter, - substituted, - arg, - true, - PsiUtil.getLanguageLevel(context)); - if (PsiType.NULL.equals(substitution) || substitution instanceof PsiWildcardType) continue; - if (substitution == null) { - result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); - return true; - } - inheritorSubstitutor = inheritorSubstitutor.put(inheritorParameter, substitution); - break; - } - } - - PsiType toAdd = createType(inheritor, inheritorSubstitutor, arrayDim); + List typeArgs = getRawSubtypes ? null : getExpectedTypeArgs(context, inheritor, Arrays.asList(inheritor.getTypeParameters()), baseType); + PsiClassType inheritorType = typeArgs == null || typeArgs.contains(null) + ? factory.createType(inheritor, factory.createRawSubstitutor(inheritor)) + : factory.createType(inheritor, typeArgs.toArray(PsiType.EMPTY_ARRAY)); + PsiType toAdd = addArrayDimensions(arrayDim, inheritorType); if (baseType.isAssignableFrom(toAdd)) { result.consume(toAdd); } + return true; }; } - private static PsiType createType(PsiClass cls, - PsiSubstitutor currentSubstitutor, - int arrayDim) { - final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(cls.getProject()).getElementFactory(); - PsiType newType = elementFactory.createType(cls, currentSubstitutor); + private static PsiType addArrayDimensions(int arrayDim, PsiType newType) { for(int i = 0; i < arrayDim; i++){ newType = newType.createArrayType(); } return newType; } + + @NotNull + public static List getExpectedTypeArgs(PsiElement context, + PsiTypeParameterListOwner paramOwner, + Iterable typeParams, PsiClassType expectedType) { + if (paramOwner instanceof PsiClass) { + PsiClassType.ClassResolveResult resolve = expectedType.resolveGenerics(); + PsiClass expectedClass = resolve.getElement(); + + if (!InheritanceUtil.isInheritorOrSelf((PsiClass)paramOwner, expectedClass, true)) { + return ContainerUtil.map(typeParams, p -> (PsiType)null); + } + + PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(expectedClass, (PsiClass)paramOwner, PsiSubstitutor.EMPTY); + assert substitutor != null; + + return ContainerUtil.map(typeParams, p -> getExpectedTypeArg(context, resolve, substitutor, p)); + } + + PsiSubstitutor substitutor = SmartCompletionDecorator.calculateMethodReturnTypeSubstitutor((PsiMethod)paramOwner, expectedType); + return ContainerUtil.map(typeParams, substitutor::substitute); + } + + @Nullable + private static PsiType getExpectedTypeArg(PsiElement context, + PsiClassType.ClassResolveResult expectedType, + PsiSubstitutor superClassSubstitutor, PsiTypeParameter typeParam) { + PsiClass expectedClass = expectedType.getElement(); + assert expectedClass != null; + for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(expectedClass)) { + PsiType paramSubstitution = superClassSubstitutor.substitute(parameter); + PsiClass inheritorCandidateParameter = PsiUtil.resolveClassInType(paramSubstitution); + if (inheritorCandidateParameter instanceof PsiTypeParameter && + ((PsiTypeParameter)inheritorCandidateParameter).getOwner() == typeParam.getOwner() && + inheritorCandidateParameter != typeParam) { + continue; + } + + PsiType argSubstitution = expectedType.getSubstitutor().substitute(parameter); + PsiType substitution = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper() + .getSubstitutionForTypeParameter(typeParam, paramSubstitution, argSubstitution, false, PsiUtil.getLanguageLevel(context)); + if (substitution != null && substitution != PsiType.NULL) { + return substitution; + } + } + return null; + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java index c16835c0cb33..c0f906c9031c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/TypeArgumentCompletionProvider.java @@ -15,10 +15,7 @@ */ package com.intellij.codeInsight.completion; -import com.intellij.codeInsight.CharTailType; -import com.intellij.codeInsight.ExpectedTypeInfo; -import com.intellij.codeInsight.ExpectedTypesProvider; -import com.intellij.codeInsight.TailType; +import com.intellij.codeInsight.*; import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementPresentation; @@ -29,7 +26,6 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; -import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -87,35 +83,13 @@ class TypeArgumentCompletionProvider extends CompletionProvider remainingParams = JBIterable.of(paramOwner.getTypeParameters()).skip(index); - List expectedArgs = getExpectedTypeArgs(context, paramOwner, remainingParams, (PsiClassType)type); + List expectedArgs = CodeInsightUtil.getExpectedTypeArgs(context, paramOwner, remainingParams, (PsiClassType)type); createLookupItems(result, context, info, expectedArgs, paramOwner); } } return true; } - @NotNull - private static List getExpectedTypeArgs(PsiElement context, - PsiTypeParameterListOwner paramOwner, - JBIterable typeParams, PsiClassType expectedType) { - if (paramOwner instanceof PsiClass) { - PsiClassType.ClassResolveResult resolve = expectedType.resolveGenerics(); - final PsiClass expectedClass = resolve.getElement(); - - if (!InheritanceUtil.isInheritorOrSelf((PsiClass)paramOwner, expectedClass, true)) { - return typeParams.map(p -> (PsiType)null).toList(); - } - - PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(expectedClass, (PsiClass)paramOwner, PsiSubstitutor.EMPTY); - assert substitutor != null; - - return typeParams.map(p -> getExpectedTypeArg(context, resolve, substitutor, p)).toList(); - } - - PsiSubstitutor substitutor = SmartCompletionDecorator.calculateMethodReturnTypeSubstitutor((PsiMethod)paramOwner, expectedType); - return typeParams.map(substitutor::substitute).toList(); - } - private void createLookupItems(Consumer result, PsiElement context, ExpectedTypeInfo info, @@ -146,24 +120,6 @@ class TypeArgumentCompletionProvider extends CompletionProvider resultSet, PsiClass referencedClass, diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments-out.java b/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments-out.java new file mode 100644 index 000000000000..8a15a8cd4554 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments-out.java @@ -0,0 +1,5 @@ +import java.util.*; + +public class SomeClass { + HashMap m = new HashMap(); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments.java b/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments.java new file mode 100644 index 000000000000..ca8a32fbf5b6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NewHashMapTypeArguments.java @@ -0,0 +1,5 @@ +import java.util.*; + +public class SomeClass { + HashMap m = new HashMap<>(); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments-out.java b/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments-out.java new file mode 100644 index 000000000000..6b008d27e0be --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments-out.java @@ -0,0 +1,5 @@ +import java.util.*; + +public class SomeClass { + Map m = new HashMap(); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments.java b/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments.java new file mode 100644 index 000000000000..296d0085898e --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NewMapTypeArguments.java @@ -0,0 +1,5 @@ +import java.util.*; + +public class SomeClass { + Map m = new HashMap<>(); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartType18CompletionTest.java index 0c38f4ce9b3e..96cd372ad3ab 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartType18CompletionTest.java @@ -255,4 +255,7 @@ public void testConvertToObjectStream() { myFixture.assertPreferredCompletionItems(0, "String.class", "tryCast"); } + public void testNewHashMapTypeArguments() { doTest(false); } + public void testNewMapTypeArguments() { doTest(false); } + } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartTypeCompletionTest.java index 685ee687fc7d..886fd3441442 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/SmartTypeCompletionTest.java @@ -1262,4 +1262,7 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { myFixture.assertPreferredCompletionItems(0, "String.class", "tryCast"); } + public void testNewHashMapTypeArguments() { doTest(); } + public void testNewMapTypeArguments() { doTest(); } + }