IDEA-182822 Raw type completion options are suggested when actual expression type is inferred

This commit is contained in:
Tagir Valeev
2017-11-29 17:59:35 +07:00
parent 58cf9d740c
commit 8673ad67aa
6 changed files with 101 additions and 39 deletions
@@ -410,13 +410,29 @@ public class GuessManagerImpl extends GuessManager {
if (fromDfa != null) {
Collection<PsiType> conjuncts = fromDfa.get(expr);
if (!conjuncts.isEmpty()) {
return ContainerUtil.newArrayList(PsiIntersectionType.flatten(conjuncts.toArray(PsiType.EMPTY_ARRAY), new LinkedHashSet<>()));
Set<PsiType> flatTypes = PsiIntersectionType.flatten(conjuncts.toArray(PsiType.EMPTY_ARRAY), new LinkedHashSet<>());
return ContainerUtil.mapNotNull(flatTypes, type -> tryGenerify(expr, type));
}
}
return Collections.emptyList();
}
private static PsiType tryGenerify(PsiExpression expression, PsiType type) {
if (!(type instanceof PsiClassType)) {
return type;
}
PsiClassType classType = (PsiClassType)type;
if (!classType.isRaw()) {
return classType;
}
PsiClass psiClass = classType.resolve();
if (psiClass == null) return classType;
PsiType expressionType = expression.getType();
if (!(expressionType instanceof PsiClassType)) return classType;
return GenericsUtil.getExpectedGenericType(expression, psiClass, (PsiClassType)expressionType);
}
private static class ExpressionTypeInstructionVisitor extends StandardInstructionVisitor {
private MultiMap<PsiExpression, PsiType> myResult;
private final PsiElement myForPlace;
@@ -29,7 +29,10 @@ import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.psi.util.FileTypeUtils;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.Consumer;
@@ -365,7 +368,8 @@ public class CodeInsightUtil {
PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY);
if (superSubstitutor == null) return true;
List<PsiType> typeArgs = getRawSubtypes ? null : getExpectedTypeArgs(context, inheritor, Arrays.asList(inheritor.getTypeParameters()), baseType);
List<PsiType> typeArgs = getRawSubtypes ? null : GenericsUtil
.getExpectedTypeArguments(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));
@@ -390,45 +394,10 @@ public class CodeInsightUtil {
PsiTypeParameterListOwner paramOwner,
Iterable<PsiTypeParameter> 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));
return GenericsUtil.getExpectedTypeArguments(context, (PsiClass)paramOwner, typeParams, expectedType);
}
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, true, PsiUtil.getLanguageLevel(context));
if (substitution != null && substitution != PsiType.NULL) {
return substitution;
}
}
return null;
}
}
@@ -551,4 +551,64 @@ public class GenericsUtil {
return !TypeConversionUtil.isAssignable(bound, type, allowUncheckedConversion);
}
}
@NotNull
public static PsiClassType getExpectedGenericType(PsiElement context,
PsiClass aClass,
PsiClassType expectedType) {
List<PsiType> arguments = getExpectedTypeArguments(context, aClass, Arrays.asList(aClass.getTypeParameters()), expectedType);
return JavaPsiFacade.getElementFactory(context.getProject()).createType(aClass, arguments.toArray(PsiType.EMPTY_ARRAY));
}
/**
* Tries to find the type parameters applied to a class which are compatible with expected supertype
*
* @param context a context element
* @param aClass a class which type parameters should be found
* @param typeParams type parameters to substitute (a subset of all type parameters of a class)
* @param expectedType an expected supertype
* @return a list of type arguments which correspond to passed type parameters
*/
@NotNull
public static List<PsiType> getExpectedTypeArguments(PsiElement context,
PsiClass aClass,
Iterable<PsiTypeParameter> typeParams,
PsiClassType expectedType) {
PsiClassType.ClassResolveResult resolve = expectedType.resolveGenerics();
PsiClass expectedClass = resolve.getElement();
if (!InheritanceUtil.isInheritorOrSelf(aClass, expectedClass, true)) {
return ContainerUtil.map(typeParams, p -> (PsiType)null);
}
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(expectedClass, aClass, PsiSubstitutor.EMPTY);
assert substitutor != null;
return ContainerUtil.map(typeParams, p -> getExpectedTypeArg(context, resolve, substitutor, p));
}
@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, true, PsiUtil.getLanguageLevel(context));
if (substitution != null && substitution != PsiType.NULL) {
return substitution;
}
}
return null;
}
}
@@ -0,0 +1,8 @@
import java.util.*;
class Foo {
void test() {
Set<String> set = new TreeSet<>();
set.ceil<caret>
}
}
@@ -0,0 +1,8 @@
import java.util.*;
class Foo {
void test() {
Set<String> set = new TreeSet<>();
((TreeSet<String>) set).ceiling()
}
}
@@ -56,6 +56,7 @@ class NormalCompletionDfaTest extends LightFixtureCompletionTestCase {
void testOptionalDfa() { doTest() }
void testFieldWithCastingCaret() { doTest() }
void testCastWhenMethodComesFromDfaSuperType() { doTest() }
void testGenericTypeDfa() { doTest() }
void testCastTwice() {
configureByTestName()