java completion: don't always cast qualifier when return type is narrowed (IDEA-186652, IDEA-186609)

only when that narrowed type is expected
This commit is contained in:
peter
2018-02-14 22:12:48 +01:00
parent c8a8eed22b
commit f8b88fec42
4 changed files with 55 additions and 16 deletions
@@ -39,6 +39,7 @@ import com.intellij.psi.util.*;
import com.intellij.psi.util.proximity.ReferenceListWeigher;
import com.intellij.ui.JBColor;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
@@ -290,6 +291,8 @@ public class JavaCompletionUtil {
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(plainQualifier);
final boolean honorExcludes = qualifierClass == null || !isInExcludedPackage(qualifierClass, false);
Set<PsiType> expectedTypes = ObjectUtils.coalesce(getExpectedTypes(parameters), Collections.emptySet());
final Set<PsiMember> mentioned = new THashSet<>();
for (CompletionElement completionElement : processor.getResults()) {
for (LookupElement item : createLookupElements(completionElement, javaReference)) {
@@ -304,7 +307,7 @@ public class JavaCompletionUtil {
}
mentioned.add(CompletionUtil.getOriginalOrSelf((PsiMember)o));
}
PsiTypeLookupItem qualifierCast = findQualifierCast(item, castItems, plainQualifier, processor);
PsiTypeLookupItem qualifierCast = findQualifierCast(item, castItems, plainQualifier, processor, expectedTypes);
if (qualifierCast != null) item = castQualifier(item, qualifierCast);
set.add(highlightIfNeeded(qualifierCast != null ? qualifierCast.getType() : plainQualifier, item, o, element));
}
@@ -356,7 +359,9 @@ public class JavaCompletionUtil {
private static boolean shouldCast(@NotNull LookupElement item,
@NotNull PsiTypeLookupItem castTypeItem,
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor) {
@Nullable PsiType plainQualifier,
@NotNull JavaCompletionProcessor processor,
@NotNull Set<PsiType> expectedTypes) {
PsiType castType = castTypeItem.getType();
if (plainQualifier != null) {
Object o = item.getObject();
@@ -379,19 +384,16 @@ public class JavaCompletionUtil {
return false;
}
PsiSubstitutor plainSub = plainResult.getSubstitutor();
PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType)castType);
if (plainMethod.getSignature(plainSub).equals(plainMethod.getSignature(castSub))) {
PsiType typeAfterCast = toRaw(castResult.getSubstitutor().substitute(method.getReturnType()));
PsiType typeDeclared = toRaw(plainSub.substitute(plainMethod.getReturnType()));
if (typeAfterCast != null && typeDeclared != null &&
typeAfterCast.isAssignableFrom(typeDeclared) &&
processor.isAccessible(plainClass.findMethodBySignature(method, true))
) {
return false;
}
if (!processor.isAccessible(plainMethod)) {
return true;
}
return true;
PsiSubstitutor castSub = TypeConversionUtil.getSuperClassSubstitutor(plainClass, (PsiClassType)castType);
PsiType typeAfterCast = toRaw(castSub.substitute(method.getReturnType()));
PsiType typeDeclared = toRaw(plainResult.getSubstitutor().substitute(plainMethod.getReturnType()));
return typeAfterCast != null && typeDeclared != null &&
!typeAfterCast.equals(typeDeclared) &&
expectedTypes.stream().anyMatch(et -> et.isAssignableFrom(typeAfterCast) && !et.isAssignableFrom(typeDeclared));
}
}
}
@@ -438,8 +440,8 @@ public class JavaCompletionUtil {
private static PsiTypeLookupItem findQualifierCast(@NotNull LookupElement item,
@NotNull List<PsiTypeLookupItem> castTypeItems,
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor) {
return ContainerUtil.find(castTypeItems, c -> shouldCast(item, c, plainQualifier, processor));
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor, Set<PsiType> expectedTypes) {
return ContainerUtil.find(castTypeItems, c -> shouldCast(item, c, plainQualifier, processor, expectedTypes));
}
@Nullable
@@ -0,0 +1,18 @@
interface TypeA { }
interface TypeB extends TypeA { }
interface ProviderOfA {
TypeA getObject();
}
interface ProviderOfB extends ProviderOfA {
TypeB getObject();
}
class IntelliJPostCastScopeBug {
void foo(ProviderOfA provider) {
if (provider instanceof ProviderOfB) {
provider.getObje<caret>
}
}
}
@@ -0,0 +1,18 @@
interface TypeA { }
interface TypeB extends TypeA { }
interface ProviderOfA {
TypeA getObject();
}
interface ProviderOfB extends ProviderOfA {
TypeB getObject();
}
class IntelliJPostCastScopeBug {
void foo(ProviderOfA provider) {
if (provider instanceof ProviderOfB) {
provider.getObject()<caret>
}
}
}
@@ -56,6 +56,7 @@ class NormalCompletionDfaTest extends NormalCompletionTestCase {
void testCastWhenMethodComesFromDfaSuperType() { doTest() }
void testGenericTypeDfa() { doTest() }
void testNarrowingReturnType() { doTest() }
void testNarrowingReturnTypeInVoidContext() { doTest() }
void testNoUnnecessaryCastDfa() { doTest() }
void testNoUnnecessaryCastRawDfa() { doTest() }
void testNoUnnecessaryCastDeepHierarchy() { doTest() }