mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-182822 Raw type completion options are suggested when actual expression type is inferred
This commit is contained in:
+17
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
+1
@@ -56,6 +56,7 @@ class NormalCompletionDfaTest extends LightFixtureCompletionTestCase {
|
||||
void testOptionalDfa() { doTest() }
|
||||
void testFieldWithCastingCaret() { doTest() }
|
||||
void testCastWhenMethodComesFromDfaSuperType() { doTest() }
|
||||
void testGenericTypeDfa() { doTest() }
|
||||
|
||||
void testCastTwice() {
|
||||
configureByTestName()
|
||||
|
||||
Reference in New Issue
Block a user