mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 09:19:13 +07:00
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:
@@ -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);
|
||||
}
|
||||
+7
-13
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-24
@@ -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()) {
|
||||
|
||||
+6
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -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) {
|
||||
|
||||
+21
@@ -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>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -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>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user