IDEA-181657 Completion items with type casts derived from control flow are not suggested if there are several nested checks from different paths of hierarchy

This commit is contained in:
peter
2017-11-08 10:01:07 +01:00
parent 6431fc6a55
commit fbbb381483
12 changed files with 160 additions and 107 deletions
@@ -21,10 +21,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.List;
public abstract class GuessManager {
public static GuessManager getInstance(Project project) {
@@ -38,8 +38,8 @@ public abstract class GuessManager {
public abstract PsiType[] guessTypeToCast(PsiExpression expr);
@NotNull
public abstract Map<PsiExpression, PsiType> getControlFlowExpressionTypes(@NotNull PsiExpression forPlace);
public abstract MultiMap<PsiExpression, PsiType> getControlFlowExpressionTypes(@NotNull PsiExpression forPlace);
@Nullable
public abstract PsiType getControlFlowExpressionType(@NotNull PsiExpression expr);
@NotNull
public abstract List<PsiType> getControlFlowExpressionTypeConjuncts(@NotNull PsiExpression expr);
}
@@ -23,13 +23,10 @@ import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashMap;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* @author peter
*/
@@ -53,7 +50,7 @@ public class ExpressionTypeMemoryState extends DfaMemoryStateImpl {
return false;
}
};
private final Map<PsiExpression, PsiType> myStates = new THashMap<>(EXPRESSION_HASHING_STRATEGY);
private final MultiMap<PsiExpression, PsiType> myStates = MultiMap.create(EXPRESSION_HASHING_STRATEGY);
public ExpressionTypeMemoryState(final DfaValueFactory factory) {
super(factory);
@@ -67,7 +64,7 @@ public class ExpressionTypeMemoryState extends DfaMemoryStateImpl {
@Override
public DfaMemoryStateImpl createCopy() {
final ExpressionTypeMemoryState copy = new ExpressionTypeMemoryState(this);
copy.myStates.putAll(myStates);
copy.myStates.putAllValues(myStates);
return copy;
}
@@ -83,7 +80,7 @@ public class ExpressionTypeMemoryState extends DfaMemoryStateImpl {
return super.applyCondition(dfaCond);
}
public Map<PsiExpression, PsiType> getStates() {
MultiMap<PsiExpression, PsiType> getStates() {
return myStates;
}
@@ -109,13 +106,10 @@ public class ExpressionTypeMemoryState extends DfaMemoryStateImpl {
@Override
public String toString() {
return super.toString() + " states=[" + new HashMap<>(myStates) + "]";
return super.toString() + " states=[" + myStates + "]";
}
public void setExpressionType(PsiExpression expression, @NotNull PsiType type) {
PsiType prev = myStates.get(expression);
if (prev == null || !type.isAssignableFrom(prev)) {
myStates.put(expression, type);
}
void setExpressionType(@NotNull PsiExpression expression, @NotNull PsiType type) {
myStates.putValue(expression, type);
}
}
@@ -35,6 +35,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.BitUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -120,34 +121,27 @@ public class GuessManagerImpl extends GuessManager {
@NotNull
@Override
public PsiType[] guessTypeToCast(PsiExpression expr) { //TODO : make better guess based on control flow
LinkedHashSet<PsiType> types = new LinkedHashSet<>();
ContainerUtil.addIfNotNull(types, getControlFlowExpressionType(expr));
public PsiType[] guessTypeToCast(PsiExpression expr) {
LinkedHashSet<PsiType> types = new LinkedHashSet<>(getControlFlowExpressionTypeConjuncts(expr));
addExprTypesWhenContainerElement(types, expr);
addExprTypesByDerivedClasses(types, expr);
return types.toArray(PsiType.createArray(types.size()));
}
@NotNull
@Override
public Map<PsiExpression, PsiType> getControlFlowExpressionTypes(@NotNull final PsiExpression forPlace) {
final Map<PsiExpression, PsiType> typeMap = buildDataflowTypeMap(forPlace);
if (typeMap != null) {
return typeMap;
}
return Collections.emptyMap();
public MultiMap<PsiExpression, PsiType> getControlFlowExpressionTypes(@NotNull final PsiExpression forPlace) {
MultiMap<PsiExpression, PsiType> typeMap = buildDataflowTypeMap(forPlace);
return typeMap != null ? typeMap : MultiMap.empty();
}
@Nullable
private static Map<PsiExpression, PsiType> buildDataflowTypeMap(PsiExpression forPlace) {
private static MultiMap<PsiExpression, PsiType> buildDataflowTypeMap(PsiExpression forPlace) {
PsiElement scope = DfaPsiUtil.getTopmostBlockInSameClass(forPlace);
if (scope == null) {
PsiFile file = forPlace.getContainingFile();
if (!(file instanceof PsiCodeFragment)) {
return Collections.emptyMap();
return MultiMap.empty();
}
scope = file;
@@ -378,24 +372,27 @@ public class GuessManagerImpl extends GuessManager {
return null;
}
@NotNull
@Override
@Nullable
public PsiType getControlFlowExpressionType(@NotNull PsiExpression expr) {
public List<PsiType> getControlFlowExpressionTypeConjuncts(@NotNull PsiExpression expr) {
final Map<PsiExpression, PsiType> allCasts = getAllTypeCasts(expr);
if (!allCasts.containsKey(expr)) {
return null; //optimization
return Collections.emptyList(); //optimization
}
final Map<PsiExpression, PsiType> fromDfa = buildDataflowTypeMap(expr);
MultiMap<PsiExpression, PsiType> fromDfa = buildDataflowTypeMap(expr);
if (fromDfa != null) {
return fromDfa.get(expr);
Collection<PsiType> conjuncts = fromDfa.get(expr);
if (!conjuncts.isEmpty()) {
return ContainerUtil.newArrayList(PsiIntersectionType.flatten(conjuncts.toArray(PsiType.EMPTY_ARRAY), new LinkedHashSet<>()));
}
}
return null;
return Collections.emptyList();
}
private static class ExpressionTypeInstructionVisitor extends InstructionVisitor {
private Map<PsiExpression, PsiType> myResult;
private MultiMap<PsiExpression, PsiType> myResult;
private final PsiElement myForPlace;
private ExpressionTypeInstructionVisitor(@NotNull PsiElement forPlace) {
@@ -407,7 +404,7 @@ public class GuessManagerImpl extends GuessManager {
}
}
public Map<PsiExpression, PsiType> getResult() {
MultiMap<PsiExpression, PsiType> getResult() {
return myResult;
}
@@ -441,9 +438,10 @@ public class GuessManagerImpl extends GuessManager {
return super.visitPush(instruction, runner, memState);
}
private void addToResult(Map<PsiExpression, PsiType> map) {
private void addToResult(MultiMap<PsiExpression, PsiType> map) {
if (myResult == null) {
myResult = new THashMap<>(map, ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY);
myResult = MultiMap.create(ExpressionTypeMemoryState.EXPRESSION_HASHING_STRATEGY);
myResult.putAllValues(map);
} else {
final Iterator<PsiExpression> iterator = myResult.keySet().iterator();
while (iterator.hasNext()) {
@@ -97,10 +97,12 @@ public class SuspiciousCollectionsMethodCallsInspection extends AbstractBaseJava
final String plainMessage = SuspiciousMethodCallUtil
.getSuspiciousMethodCallMessage(methodCall, args[0], argType, exactType || reportConvertibleMethodCalls, patternMethods, indices);
if (plainMessage != null && !exactType) {
final PsiType dfaType = GuessManager.getInstance(methodCall.getProject()).getControlFlowExpressionType(args[0]);
if (dfaType != null && SuspiciousMethodCallUtil
.getSuspiciousMethodCallMessage(methodCall, args[0], dfaType, reportConvertibleMethodCalls, patternMethods, indices) == null) {
return null;
List<PsiType> dfaTypes = GuessManager.getInstance(methodCall.getProject()).getControlFlowExpressionTypeConjuncts(args[0]);
for (PsiType dfaType : dfaTypes) {
if (SuspiciousMethodCallUtil
.getSuspiciousMethodCallMessage(methodCall, args[0], dfaType, reportConvertibleMethodCalls, patternMethods, indices) == null) {
return null;
}
}
}
@@ -31,11 +31,10 @@ import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author peter
*/
@@ -96,7 +95,7 @@ public class BasicExpressionCompletionContributor {
final PsiExpression context = PsiTreeUtil.getParentOfType(position, PsiExpression.class);
if (context == null) return;
final Map<PsiExpression,PsiType> map = GuessManager.getInstance(position.getProject()).getControlFlowExpressionTypes(context);
MultiMap<PsiExpression,PsiType> map = GuessManager.getInstance(position.getProject()).getControlFlowExpressionTypes(context);
if (map.isEmpty()) {
return;
}
@@ -114,8 +113,7 @@ public class BasicExpressionCompletionContributor {
PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)expression;
final PsiExpression operand = typeCastExpression.getOperand();
if (operand != null) {
final PsiType dfaCasted = map.get(operand);
if (dfaCasted != null && dfaCasted.equals(typeCastExpression.getType())) {
if (map.get(operand).contains(typeCastExpression.getType())) {
map.remove(operand);
}
}
@@ -125,11 +123,12 @@ public class BasicExpressionCompletionContributor {
}
}, context, context.getContainingFile());
for (final PsiExpression expression : map.keySet()) {
final PsiType castType = map.get(expression);
final PsiType baseType = expression.getType();
if (expectedType == null || (expectedType.isAssignableFrom(castType) && (baseType == null || !expectedType.isAssignableFrom(baseType)))) {
consumer.consume(CastingLookupElementDecorator.createCastingElement(expressionToLookupElement(expression), castType));
for (PsiExpression expression : map.keySet()) {
for (PsiType castType : map.get(expression)) {
PsiType baseType = expression.getType();
if (expectedType == null || (expectedType.isAssignableFrom(castType) && (baseType == null || !expectedType.isAssignableFrom(baseType)))) {
consumer.consume(CastingLookupElementDecorator.createCastingElement(expressionToLookupElement(expression), castType));
}
}
}
}
@@ -103,9 +103,9 @@ public class JavaCompletionSorting {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
ExpectedTypeInfo castExpectation = SmartCastProvider.getParenthesizedCastExpectationByOperandType(position);
if (castExpectation != null) {
return new ExpectedTypeInfo[]{castExpectation};
List<ExpectedTypeInfo> castExpectation = SmartCastProvider.getParenthesizedCastExpectationByOperandType(position);
if (!castExpectation.isEmpty()) {
return castExpectation.toArray(ExpectedTypeInfo.EMPTY_ARRAY);
}
return JavaSmartCompletionContributor.getExpectedTypes(parameters);
}
@@ -59,6 +59,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.siyeh.ig.psiutils.SideEffectChecker;
import gnu.trove.THashSet;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -285,30 +286,28 @@ public class JavaCompletionUtil {
final JavaCompletionProcessor processor = new JavaCompletionProcessor(element, elementFilter, options, nameCondition);
final PsiType plainQualifier = processor.getQualifierType();
PsiType qualifierType = plainQualifier;
PsiType runtimeQualifier = getQualifierCastType(javaReference, parameters);
if (runtimeQualifier != null) {
PsiType composite = qualifierType == null ? runtimeQualifier : PsiIntersectionType.createIntersection(qualifierType, runtimeQualifier);
List<PsiType> runtimeQualifiers = getQualifierCastTypes(javaReference, parameters);
if (!runtimeQualifiers.isEmpty()) {
PsiType composite = PsiIntersectionType.createIntersection(JBIterable.of(plainQualifier).append(runtimeQualifiers).toList());
PsiElement ctx = createContextWithXxxVariable(element, composite);
javaReference = createReference("xxx.xxx", ctx);
qualifierType = runtimeQualifier;
processor.setQualifierType(qualifierType);
processor.setQualifierType(composite);
}
javaReference.processVariants(processor);
final PsiTypeLookupItem castItem = runtimeQualifier == null ? null : PsiTypeLookupItem.createLookupItem(runtimeQualifier, (PsiReferenceExpression)javaReference);
List<PsiTypeLookupItem> castItems = ContainerUtil.map(runtimeQualifiers, q -> PsiTypeLookupItem.createLookupItem(q, element));
final boolean pkgContext = inSomePackage(element);
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifierType);
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(plainQualifier);
final boolean honorExcludes = qualifierClass == null || !isInExcludedPackage(qualifierClass, false);
final Set<PsiMember> mentioned = new THashSet<>();
for (CompletionElement completionElement : processor.getResults()) {
for (LookupElement item : createLookupElements(completionElement, javaReference)) {
item.putUserData(QUALIFIER_TYPE_ATTR, qualifierType);
item.putUserData(QUALIFIER_TYPE_ATTR, plainQualifier);
final Object o = item.getObject();
if (o instanceof PsiClass && !isSourceLevelAccessible(element, (PsiClass)o, pkgContext)) {
continue;
@@ -319,7 +318,9 @@ public class JavaCompletionUtil {
}
mentioned.add(CompletionUtil.getOriginalOrSelf((PsiMember)o));
}
set.add(highlightIfNeeded(qualifierType, castQualifier(item, castItem, plainQualifier, processor), o, element));
PsiTypeLookupItem qualifierCast = findQualifierCast(item, castItems, plainQualifier, processor);
if (qualifierCast != null) item = castQualifier(item, qualifierCast);
set.add(highlightIfNeeded(qualifierCast != null ? qualifierCast.getType() : plainQualifier, item, o, element));
}
}
@@ -346,38 +347,34 @@ public class JavaCompletionUtil {
return (PsiReferenceExpression) JavaPsiFacade.getElementFactory(context.getProject()).createExpressionFromText(text, context);
}
@Nullable
private static PsiType getQualifierCastType(PsiJavaReference javaReference, CompletionParameters parameters) {
@NotNull
private static List<PsiType> getQualifierCastTypes(PsiJavaReference javaReference, CompletionParameters parameters) {
if (javaReference instanceof PsiReferenceExpression) {
final PsiReferenceExpression refExpr = (PsiReferenceExpression)javaReference;
final PsiExpression qualifier = refExpr.getQualifierExpression();
if (qualifier != null) {
final Project project = qualifier.getProject();
PsiType type = null;
final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
if (evaluator != null) {
type = evaluator.fun(qualifier, parameters);
PsiType type = evaluator.fun(qualifier, parameters);
if (type != null) {
return Collections.singletonList(type);
}
}
if (type == null) {
type = GuessManager.getInstance(project).getControlFlowExpressionType(qualifier);
}
return type;
return GuessManager.getInstance(project).getControlFlowExpressionTypeConjuncts(qualifier);
}
}
return null;
return Collections.emptyList();
}
@NotNull
private static LookupElement castQualifier(@NotNull LookupElement item,
@Nullable final PsiTypeLookupItem castTypeItem,
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor) {
if (castTypeItem == null) {
return item;
}
private static boolean shouldCast(@NotNull LookupElement item,
@NotNull PsiTypeLookupItem castTypeItem,
@Nullable PsiType plainQualifier, JavaCompletionProcessor processor) {
PsiType castType = castTypeItem.getType();
if (plainQualifier != null) {
Object o = item.getObject();
if (o instanceof PsiMethod) {
PsiType castType = castTypeItem.getType();
if (plainQualifier instanceof PsiClassType && castType instanceof PsiClassType) {
PsiMethod method = (PsiMethod)o;
PsiClassType.ClassResolveResult plainResult = ((PsiClassType)plainQualifier).resolveGenerics();
@@ -386,7 +383,7 @@ public class JavaCompletionUtil {
PsiClass castClass = ((PsiClassType)castType).resolveGenerics().getElement();
if (castClass == null || !castClass.isInheritor(plainClass, true)) {
return item;
return false;
}
PsiSubstitutor plainSub = plainResult.getSubstitutor();
@@ -395,20 +392,25 @@ public class JavaCompletionUtil {
if (method.getSignature(plainSub).equals(method.getSignature(castSub))) {
PsiType typeAfterCast = toRaw(castSub.substitute(returnType));
PsiType typeDeclared = toRaw(plainSub.substitute(returnType));
if (typeAfterCast != null && typeDeclared != null &&
if (typeAfterCast != null && typeDeclared != null &&
typeAfterCast.isAssignableFrom(typeDeclared) &&
processor.isAccessible(plainClass.findMethodBySignature(method, true))
) {
return item;
return false;
}
}
return true;
}
}
} else if (containsMember(plainQualifier, o)) {
return item;
}
return containsMember(castType, o) && !containsMember(plainQualifier, o);
}
return false;
}
@NotNull
private static LookupElement castQualifier(@NotNull LookupElement item, @NotNull PsiTypeLookupItem castTypeItem) {
return LookupElementDecorator.withInsertHandler(item, new InsertHandlerDecorator<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElementDecorator<LookupElement> item) {
@@ -441,7 +443,13 @@ 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
private static PsiType toRaw(@Nullable PsiType type) {
return type instanceof PsiClassType ? ((PsiClassType)type).rawType() : type;
@@ -461,7 +469,7 @@ public class JavaCompletionUtil {
}
}), -1);
}
if (containsMember(qualifierType, object)) {
if (containsMember(qualifierType, object) && !qualifierType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
LookupElementDecorator<LookupElement> bold = LookupElementDecorator.withRenderer(item, new LookupElementRenderer<LookupElementDecorator<LookupElement>>() {
@Override
public void renderElement(LookupElementDecorator<LookupElement> element, LookupElementPresentation presentation) {
@@ -487,6 +495,7 @@ public class JavaCompletionUtil {
return false;
}
@Contract("null, _ -> false")
public static boolean containsMember(@Nullable PsiType qualifierType, @NotNull Object object) {
if (qualifierType instanceof PsiArrayType && object instanceof PsiMember) { //length and clone()
PsiFile file = ((PsiMember)object).getContainingFile();
@@ -24,10 +24,11 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collections;
import java.util.List;
import static com.intellij.patterns.PlatformPatterns.psiElement;
@@ -72,8 +73,7 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
result.consume(PsiTypeLookupItem.createLookupItem(info.getType(), parent));
}
}
ExpectedTypeInfo info = getParenthesizedCastExpectationByOperandType(position);
if (info != null) {
for (ExpectedTypeInfo info : getParenthesizedCastExpectationByOperandType(position)) {
addHierarchyTypes(parameters, matcher, info, type -> result.consume(PsiTypeLookupItem.createLookupItem(type, parent)), quick);
}
return;
@@ -103,20 +103,21 @@ class SmartCastProvider extends CompletionProvider<CompletionParameters> {
}
}
@Nullable
static ExpectedTypeInfo getParenthesizedCastExpectationByOperandType(PsiElement position) {
@NotNull
static List<ExpectedTypeInfo> getParenthesizedCastExpectationByOperandType(PsiElement position) {
PsiElement parenthesisOwner = getParenthesisOwner(position);
PsiExpression operand = getCastedExpression(parenthesisOwner);
if (operand == null || !(parenthesisOwner.getParent() instanceof PsiParenthesizedExpression)) return null;
if (operand == null || !(parenthesisOwner.getParent() instanceof PsiParenthesizedExpression)) return Collections.emptyList();
PsiType dfaType = GuessManager.getInstance(operand.getProject()).getControlFlowExpressionType(operand);
if (dfaType != null) {
return new ExpectedTypeInfoImpl(dfaType, ExpectedTypeInfo.TYPE_OR_SUPERTYPE, dfaType, TailType.NONE, null, () -> null);
List<PsiType> dfaTypes = GuessManager.getInstance(operand.getProject()).getControlFlowExpressionTypeConjuncts(operand);
if (!dfaTypes.isEmpty()) {
return ContainerUtil.map(dfaTypes, dfaType ->
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);
return type == null || type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ? Collections.emptyList() :
Collections.singletonList(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, boolean quick) {
@@ -0,0 +1,21 @@
interface Common {
String moo();
}
interface I1 extends Common {
String foo();
}
interface I2 extends Common {
String boo();
}
public class Main {
public static void test(Common param) {
if (param instanceof I1) {
if (param instanceof I2) {
param.<caret>
}
}
}
}
@@ -0,0 +1,21 @@
interface Common {
String moo();
}
interface I1 extends Common {
String foo();
}
interface I2 extends Common {
String boo();
}
public class Main {
public static void test(Common param) {
if (param instanceof I1) {
if (param instanceof I2) {
((I1) param).foo()<caret>
}
}
}
}
@@ -76,6 +76,14 @@ public class FooImpl extends Foo {
void testCastInstanceofedQualifierInLambda2() { doTest() }
void testCastInstanceofedQualifierInExpressionLambda() { doTest() }
void testCastQualifierInstanceofedTwice() {
configureByTestName()
myFixture.assertPreferredCompletionItems 0, 'boo', 'foo', 'moo'
myFixture.lookup.currentItem = myFixture.lookupElements[1]
myFixture.type('\n')
checkResultByFile(getTestName(false) + "_after.java")
}
void testPreferCastExpressionSuperTypes() {
myFixture.addClass('package nonImported; public interface SzNameInTheEnd {}')
@@ -389,7 +389,7 @@ public class MultiMap<K, V> implements Serializable {
@Override
public String toString() {
return myMap.toString();
return new java.util.HashMap<K, Collection<V>>(myMap).toString();
}
/**