mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java][switch completion] IDEA-278622 Fix completion variants in switch case label
Use the `inheritorsFilter` when the element that is being filtered is of the PsiClass type regardless if the selector class of a switch block is sealed or not. All the inheritors of the sealed class get added in JavaCompletionProcessor. GitOrigin-RevId: 28e70b44444edad329f086a9dca0557baea03bec
This commit is contained in:
committed by
intellij-monorepo-bot
parent
13dec23fbc
commit
90db265d7a
+5
-38
@@ -64,7 +64,6 @@ import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.SealedUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import gnu.trove.THashSet;
|
||||
import one.util.streamex.EntryStream;
|
||||
@@ -109,22 +108,6 @@ public final class JavaCompletionContributor extends CompletionContributor imple
|
||||
return aClass != null && aClass.isEnum();
|
||||
}
|
||||
}))));
|
||||
private static final ElementPattern<PsiElement> IN_SEALED_SWITCH = psiElement()
|
||||
.withSuperParent(2, psiElement(PsiCaseLabelElementList.class)
|
||||
.withParent(psiElement(PsiSwitchLabelStatementBase.class)
|
||||
.withSuperParent(2, psiElement(PsiSwitchBlock.class)
|
||||
.with(new PatternCondition<>("sealedExpressionType") {
|
||||
@Override
|
||||
public boolean accepts(@NotNull PsiSwitchBlock psiSwitchBlock, ProcessingContext context) {
|
||||
final PsiExpression expression = psiSwitchBlock.getExpression();
|
||||
if (expression == null) return false;
|
||||
final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
|
||||
return aClass != null && aClass.hasModifierProperty(PsiModifier.SEALED);
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
private static final PsiJavaElementPattern.Capture<PsiElement> IN_CASE_LABEL_ELEMENT_LIST =
|
||||
psiElement().withSuperParent(2, psiElement(PsiCaseLabelElementList.class));
|
||||
|
||||
@@ -308,28 +291,12 @@ public final class JavaCompletionContributor extends CompletionContributor imple
|
||||
return new OrFilter(new ClassFilter(PsiClass.class), constantVariablesFilter);
|
||||
}
|
||||
|
||||
final PsiResolveHelper resolver = JavaPsiFacade.getInstance(position.getProject()).getResolveHelper();
|
||||
final PsiClass selectorClass = resolver.resolveReferencedClass(selectorType.getCanonicalText(), null);
|
||||
if (selectorClass == null) return TrueFilter.INSTANCE;
|
||||
|
||||
if (IN_SEALED_SWITCH.accepts(position)) {
|
||||
final Collection<PsiClass> classes = SealedUtils.findSameFileInheritorsClasses(selectorClass);
|
||||
return new ClassFilter(PsiClass.class) {
|
||||
@Override
|
||||
public boolean isAcceptable(Object element, PsiElement context) {
|
||||
if (!(element instanceof PsiClass)) return false;
|
||||
|
||||
final PsiClass aClass = (PsiClass)element;
|
||||
|
||||
return selectorClass == aClass || classes.contains(aClass);
|
||||
}
|
||||
};
|
||||
}
|
||||
final PsiClass typeClass = PsiUtil.resolveClassInType(selectorType);
|
||||
|
||||
final ClassFilter inheritorsFilter = new ClassFilter(PsiClass.class) {
|
||||
@Override
|
||||
public boolean isAcceptable(Object element, PsiElement context) {
|
||||
return element instanceof PsiClass && InheritanceUtil.isInheritorOrSelf((PsiClass)element, selectorClass, true);
|
||||
return element instanceof PsiClass && InheritanceUtil.isInheritorOrSelf((PsiClass)element, typeClass, true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -337,9 +304,9 @@ public final class JavaCompletionContributor extends CompletionContributor imple
|
||||
return new OrFilter(constantVariablesFilter, inheritorsFilter);
|
||||
}
|
||||
|
||||
if (selectorType instanceof PsiClassType) return inheritorsFilter;
|
||||
|
||||
return TrueFilter.INSTANCE;
|
||||
return selectorType instanceof PsiClassType
|
||||
? inheritorsFilter
|
||||
: TrueFilter.INSTANCE;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
|
||||
+26
-3
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.completion.scope;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.PsiMethodReferenceHighlightingUtil;
|
||||
@@ -26,13 +26,17 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.LinkedHashMap;
|
||||
import com.siyeh.ig.psiutils.SealedUtils;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature.PATTERNS_IN_SWITCH;
|
||||
|
||||
public final class JavaCompletionProcessor implements PsiScopeProcessor, ElementClassHint {
|
||||
private static final Logger LOG = Logger.getInstance(JavaCompletionProcessor.class);
|
||||
|
||||
@@ -192,9 +196,28 @@ public final class JavaCompletionProcessor implements PsiScopeProcessor, Element
|
||||
}
|
||||
}
|
||||
|
||||
if (!(element instanceof PsiClass) || !PATTERNS_IN_SWITCH.isAvailable(myElement)) return true;
|
||||
|
||||
final PsiClass psiClass = (PsiClass)element;
|
||||
if (psiClass.hasModifierProperty(PsiModifier.SEALED)) {
|
||||
addSealedHierarchy(state, psiClass);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private void addSealedHierarchy(@NotNull ResolveState state, @NotNull PsiClass psiClass) {
|
||||
final Collection<PsiClass> sealedInheritors = SealedUtils.findSameFileInheritorsClasses(psiClass);
|
||||
for (PsiClass inheritor : sealedInheritors) {
|
||||
final CompletionElement completion = new CompletionElement(inheritor, state.get(PsiSubstitutor.KEY));
|
||||
final CompletionElement prev = myResults.get(completion);
|
||||
|
||||
if (prev == null || completion.isMoreSpecificThan(prev)) {
|
||||
myResults.put(completion, completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiType getMethodReferenceType(PsiElement completion) {
|
||||
PsiElement parent = myElement.getParent();
|
||||
@@ -252,7 +275,7 @@ public final class JavaCompletionProcessor implements PsiScopeProcessor, Element
|
||||
PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)element;
|
||||
if (myStatic) {
|
||||
if (!(element instanceof PsiClass) && !modifierListOwner.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
// we don't need non static method in static context.
|
||||
// we don't need non-static method in static context.
|
||||
return StaticProblem.instanceAfterStatic;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +317,7 @@ public final class JavaCompletionProcessor implements PsiScopeProcessor, Element
|
||||
|
||||
public boolean isAccessible(@Nullable final PsiElement element) {
|
||||
// if checkAccess is false, we only show inaccessible source elements because their access modifiers can be changed later by the user.
|
||||
// compiled element can't be changed so we don't pollute the completion with them. In Javadoc, everything is allowed.
|
||||
// compiled element can't be changed, so we don't pollute the completion with them. In Javadoc, everything is allowed.
|
||||
if (!myOptions.checkAccess && myInJavaDoc) return true;
|
||||
|
||||
if (isAccessibleForResolve(element)) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
class Main {
|
||||
public static abstract sealed class Sealed {
|
||||
public static final class SealedInheritor extends Sealed {}
|
||||
}
|
||||
|
||||
int f(Sealed o) {
|
||||
return switch(o) {
|
||||
case Sealed.SealedIn<caret>, null
|
||||
}
|
||||
}
|
||||
|
||||
int g(Sealed o) {
|
||||
return switch(o) {
|
||||
case null, Sealed.SealedIn<caret>
|
||||
}
|
||||
}
|
||||
|
||||
int h(Sealed o) {
|
||||
return switch(o) {
|
||||
case Sealed.SealedIn<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
class Main {
|
||||
public static abstract sealed class Sealed {
|
||||
public static final class SealedInheritor extends Sealed {}
|
||||
}
|
||||
|
||||
int f(Sealed o) {
|
||||
return switch(o) {
|
||||
case Sealed.SealedInheritor, null
|
||||
}
|
||||
}
|
||||
|
||||
int g(Sealed o) {
|
||||
return switch(o) {
|
||||
case null, Sealed.SealedInheritor
|
||||
}
|
||||
}
|
||||
|
||||
int h(Sealed o) {
|
||||
return switch(o) {
|
||||
case Sealed.SealedInheritor
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -3,6 +3,7 @@ package com.intellij.java.codeInsight.completion
|
||||
|
||||
import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.NeedsIndex
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.annotations.NotNull
|
||||
|
||||
@@ -24,12 +25,16 @@ class NormalSwitchCompletionTest extends NormalCompletionTestCase {
|
||||
void testInsideRuleInSwitchExpression() { doTest() }
|
||||
void testBreakDeepInsideSwitchExpression() { doTest() }
|
||||
|
||||
@NeedsIndex.Full
|
||||
void testCompletePatternVariableInSwitchExpr() { doTest() }
|
||||
@NeedsIndex.Full
|
||||
void testCompletePatternVariableInSwitchStmt() { doTest() }
|
||||
|
||||
void testCompleteReturnInSwitch() { doTest() }
|
||||
|
||||
@NeedsIndex.Full
|
||||
void testCompleteConstantInSwitchExpr() { doTest() }
|
||||
@NeedsIndex.Full
|
||||
void testCompleteConstantInSwitchStmt() { doTest() }
|
||||
|
||||
void testCompleteNullInSwitchStmt() { doTest() }
|
||||
@@ -41,13 +46,15 @@ class NormalSwitchCompletionTest extends NormalCompletionTestCase {
|
||||
void testCompletePatternVariableSwitchStmt() { doTest() }
|
||||
void testCompletePatternVariableSwitchExpr() { doTest() }
|
||||
|
||||
void testCompleteSealedLabelSwitch() { doTest() }
|
||||
|
||||
void testCompleteSwitchObjectSelectorPostfix() { doTestPostfixCompletion() }
|
||||
void testCompleteSwitchSealedSelectorPostfix() { doTestPostfixCompletion() }
|
||||
|
||||
private void doTestPostfixCompletion() {
|
||||
LiveTemplateCompletionContributor.setShowTemplatesInTests(true, myFixture.testRootDisposable)
|
||||
configure();
|
||||
configure()
|
||||
myFixture.type('\t' as char)
|
||||
checkResult();
|
||||
checkResult()
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -67,7 +67,7 @@ public final class SealedUtils {
|
||||
private static @NotNull <T> Collection<T> getClasses(@NotNull PsiClass psiClass,
|
||||
Function<PsiClass, T> mapper,
|
||||
PsiClass @NotNull ... classesToExclude) {
|
||||
GlobalSearchScope fileScope = GlobalSearchScope.fileScope(psiClass.getContainingFile());
|
||||
GlobalSearchScope fileScope = GlobalSearchScope.fileScope(psiClass.getContainingFile().getOriginalFile());
|
||||
return DirectClassInheritorsSearch.search(psiClass, fileScope)
|
||||
.filtering(inheritor -> !ArrayUtil.contains(inheritor, classesToExclude))
|
||||
.mapping(mapper)
|
||||
|
||||
Reference in New Issue
Block a user