prefer relevant types when completing cast type (IDEA-167190, IDEA-156868)

This commit is contained in:
peter
2017-01-31 19:23:12 +01:00
parent 476734465d
commit f66c322e16
7 changed files with 124 additions and 26 deletions
@@ -290,7 +290,7 @@ public class JavaCompletionContributor extends CompletionContributor {
}
if (SmartCastProvider.shouldSuggestCast(parameters)) {
SmartCastProvider.addCastVariants(parameters, element -> {
SmartCastProvider.addCastVariants(parameters, result.getPrefixMatcher(), element -> {
registerClassFromTypeElement(element, session);
result.addElement(PrioritizedLookupElement.withPriority(element, 1));
});
@@ -24,7 +24,6 @@ import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.NameUtil;
@@ -43,6 +42,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
/**
* @author peter
*/
@@ -51,11 +52,11 @@ public class JavaCompletionSorting {
}
public static CompletionResultSet addJavaSorting(final CompletionParameters parameters, CompletionResultSet result) {
final PsiElement position = parameters.getPosition();
final ExpectedTypeInfo[] expectedTypes = PsiJavaPatterns.psiElement().beforeLeaf(PsiJavaPatterns.psiElement().withText(".")).accepts(position) ? ExpectedTypeInfo.EMPTY_ARRAY : JavaSmartCompletionContributor.getExpectedTypes(parameters);
final CompletionType type = parameters.getCompletionType();
final boolean smart = type == CompletionType.SMART;
final boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(position);
PsiElement position = parameters.getPosition();
ExpectedTypeInfo[] expectedTypes = getExpectedTypesWithDfa(parameters, position);
CompletionType type = parameters.getCompletionType();
boolean smart = type == CompletionType.SMART;
boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(position);
List<LookupElementWeigher> afterProximity = new ArrayList<>();
afterProximity.add(new PreferContainingSameWords(expectedTypes));
@@ -91,6 +92,18 @@ public class JavaCompletionSorting {
return result.withRelevanceSorter(sorter);
}
private static ExpectedTypeInfo[] getExpectedTypesWithDfa(CompletionParameters parameters, PsiElement position) {
if (psiElement().beforeLeaf(psiElement().withText(".")).accepts(position)) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
ExpectedTypeInfo castExpectation = SmartCastProvider.getParenthesizedCastExpectationByOperandType(position);
if (castExpectation != null) {
return new ExpectedTypeInfo[]{castExpectation};
}
return JavaSmartCompletionContributor.getExpectedTypes(parameters);
}
@Nullable
private static LookupElementWeigher recursion(CompletionParameters parameters, final ExpectedTypeInfo[] expectedInfos) {
final PsiElement position = parameters.getPosition();
@@ -147,17 +160,20 @@ public class JavaCompletionSorting {
PsiUtil.ensureValidType(itemType);
for (final ExpectedTypeInfo expectedInfo : expectedInfos) {
final PsiType defaultType = expectedInfo.getDefaultType();
final PsiType expectedType = expectedInfo.getType();
PsiType expectedType = expectedInfo.getType();
assert expectedType.isValid();
assert defaultType.isValid();
if (defaultType != expectedType && defaultType.isAssignableFrom(itemType)) {
return ExpectedTypeMatching.ofDefaultType;
}
if (expectedType.isAssignableFrom(itemType)) {
return ExpectedTypeMatching.expected;
if (expectedInfo.getKind() == ExpectedTypeInfo.TYPE_OR_SUPERTYPE) {
if (itemType.isAssignableFrom(expectedType)) {
return ExpectedTypeMatching.expected;
}
} else {
PsiType defaultType = expectedInfo.getDefaultType();
if (defaultType != expectedType && defaultType.isAssignableFrom(itemType)) {
return ExpectedTypeMatching.ofDefaultType;
}
if (expectedType.isAssignableFrom(itemType)) {
return ExpectedTypeMatching.expected;
}
}
}
}
@@ -480,7 +496,10 @@ public class JavaCompletionSorting {
}
}
}
return preferByMemberName(myExpectedMemberName, itemType);
ExpectedTypeMatching byName = preferByMemberName(myExpectedMemberName, itemType);
if (byName != ExpectedTypeMatching.normal) {
return byName;
}
}
return getExpectedTypeMatching(item, myExpectedTypes, myExpectedMemberName);
@@ -1,9 +1,11 @@
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.ExpectedTypesProvider;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.simple.RParenthTailType;
import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementDecorator;
@@ -17,12 +19,16 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.tree.java.PsiEmptyExpressionImpl;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.patterns.PlatformPatterns.psiElement;
/**
@@ -48,10 +54,10 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
@Override
protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
addCastVariants(parameters, result);
addCastVariants(parameters, result.getPrefixMatcher(), result);
}
static void addCastVariants(@NotNull CompletionParameters parameters, @NotNull Consumer<LookupElement> result) {
static void addCastVariants(@NotNull CompletionParameters parameters, PrefixMatcher matcher, @NotNull Consumer<LookupElement> result) {
if (!shouldSuggestCast(parameters)) return;
PsiElement position = parameters.getPosition();
@@ -60,9 +66,15 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
if (insideCast) {
PsiElement parent = parenthesisOwner.getParent();
if (parent instanceof PsiParenthesizedExpression && parent.getParent() instanceof PsiReferenceExpression) {
for (ExpectedTypeInfo info : ExpectedTypesProvider.getExpectedTypes((PsiParenthesizedExpression)parent, false)) {
result.consume(PsiTypeLookupItem.createLookupItem(info.getType(), parent));
if (parent instanceof PsiParenthesizedExpression) {
if (parent.getParent() instanceof PsiReferenceExpression) {
for (ExpectedTypeInfo info : ExpectedTypesProvider.getExpectedTypes((PsiParenthesizedExpression)parent, false)) {
result.consume(PsiTypeLookupItem.createLookupItem(info.getType(), parent));
}
}
ExpectedTypeInfo info = getParenthesizedCastExpectationByOperandType(position);
if (info != null) {
addHierarchyTypes(parameters, matcher, info, type -> result.consume(PsiTypeLookupItem.createLookupItem(type, parent)));
}
return;
}
@@ -92,10 +104,48 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
}
@Nullable
static ExpectedTypeInfo getParenthesizedCastExpectationByOperandType(PsiElement position) {
PsiElement parenthesisOwner = getParenthesisOwner(position);
PsiExpression operand = getCastedExpression(parenthesisOwner);
if (operand == null || !(parenthesisOwner.getParent() instanceof PsiParenthesizedExpression)) return null;
PsiType dfaType = GuessManager.getInstance(operand.getProject()).getControlFlowExpressionType(operand);
if (dfaType != null) {
return new ExpectedTypeInfoImpl(dfaType, ExpectedTypeInfo.TYPE_OR_SUPERTYPE, dfaType, TailType.NONE, null, () -> null);
}
PsiType type = operand.getType();
return type == null || type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ? null :
new ExpectedTypeInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.NONE, null, () -> null);
}
private static void addHierarchyTypes(CompletionParameters parameters, PrefixMatcher matcher, ExpectedTypeInfo info, Consumer<PsiType> result) {
PsiType infoType = info.getType();
PsiClass infoClass = PsiUtil.resolveClassInClassTypeOnly(infoType);
if (info.getKind() == ExpectedTypeInfo.TYPE_OR_SUPERTYPE) {
InheritanceUtil.processSupers(infoClass, true, superClass -> {
if (!CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
result.consume(JavaPsiFacade.getElementFactory(superClass.getProject()).createType(superClass));
}
return true;
});
} else {
JavaInheritorsGetter.processInheritors(parameters, Collections.singleton((PsiClassType)infoType), matcher, type -> {
if (!infoType.equals(type)) {
result.consume(type);
}
});
}
}
private static PsiType getCastedExpressionType(PsiElement parenthesisOwner) {
PsiExpression operand = getCastedExpression(parenthesisOwner);
return operand == null ? null : operand.getType();
}
private static PsiExpression getCastedExpression(PsiElement parenthesisOwner) {
if (parenthesisOwner instanceof PsiTypeCastExpression) {
final PsiExpression operand = ((PsiTypeCastExpression)parenthesisOwner).getOperand();
return operand == null ? null : operand.getType();
return ((PsiTypeCastExpression)parenthesisOwner).getOperand();
}
if (parenthesisOwner instanceof PsiParenthesizedExpression) {
@@ -104,7 +154,7 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
next = next.getNextSibling();
}
if (next instanceof PsiExpression) {
return ((PsiExpression)next).getType();
return (PsiExpression)next;
}
}
return null;
@@ -0,0 +1,11 @@
public class Super {
static void foo(Object o) {
if (o instanceof SString) {
((S<caret>) o)
}
}
}
class SString implements java.io.Serializable, nonImported.SzNameInTheEnd {}
@@ -0,0 +1,7 @@
public class Super {
static void foo(CharSequence o) {
((<caret>) o)
}
}
@@ -74,4 +74,10 @@ public class FooImpl extends Foo {
void testCastInstanceofedQualifierInExpressionLambda() { doTest() }
void testPreferCastExpressionSuperTypes() {
myFixture.addClass('package nonImported; public interface SzNameInTheEnd {}')
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'SString', 'SzNameInTheEnd', 'Serializable'
}
}
@@ -1250,4 +1250,9 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase {
}
public void testQualifyOuterClassCall() { doActionTest(); }
public void testExpressionSubtypesInCast() {
configureByTestName();
myFixture.assertPreferredCompletionItems(0, "String", "StringBuffer", "StringBuilder");
}
}