[java-intentions] IDEA-313226. Suggest the correct type for switch. Generics and tests

GitOrigin-RevId: 4814c85fd7721fb7866210825a0d2286c61e2043
This commit is contained in:
Mikhail Pyltsin
2023-02-20 18:12:13 +01:00
committed by intellij-monorepo-bot
parent ce216843a0
commit 016ba5fb84
8 changed files with 210 additions and 8 deletions

View File

@@ -619,20 +619,33 @@ public final class ExpectedTypesProvider {
}
}
result.addAll(processedExpressionTypes(labeledExpressionTypes, mustBeReference, statement));
result.addAll(processedPatternTypes(labeledPatternsTypes));
result.addAll(processedPatternTypes(labeledPatternsTypes, statement));
return result;
}
private static List<ExpectedTypeInfo> processedPatternTypes(@NotNull List<PsiType> expectedTypes) {
LinkedHashSet<PsiClass> processedTypes = new LinkedHashSet<>();
private static List<ExpectedTypeInfo> processedPatternTypes(@NotNull List<PsiType> expectedTypes, @NotNull PsiSwitchBlock statement) {
LinkedHashSet<PsiType> processedTypes = new LinkedHashSet<>();
for (PsiType type : expectedTypes) {
PsiClass currentClass = PsiUtil.resolveClassInClassTypeOnly(type);
if (currentClass == null) {
continue;
}
LinkedHashSet<PsiClass> allSuperClasses = InheritanceUtil.getSuperClasses(currentClass);
if (!(type instanceof PsiClassType classType)) {
continue;
}
PsiSubstitutor substitutor = classType.resolveGenerics().getSubstitutor();
LinkedHashSet<PsiType> allSuperClasses = new LinkedHashSet<>();
InheritanceUtil.processSuperTypes(classType, true, nextType -> {
PsiClass superClass = PsiUtil.resolveClassInClassTypeOnly(nextType);
if (superClass != null) {
PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(superClass, currentClass, substitutor);
PsiType substituted = superClassSubstitutor.substitute(nextType);
allSuperClasses.add(substituted);
}
return true;
});
if (processedTypes.isEmpty()) {
processedTypes = allSuperClasses;
}
@@ -642,9 +655,13 @@ public final class ExpectedTypesProvider {
}
List<ExpectedTypeInfo> result = new ArrayList<>();
for (PsiClass psiClass : processedTypes) {
PsiClassType type = PsiTypesUtil.getClassType(psiClass);
result.add(createInfo(type, ExpectedTypeInfo.TYPE_STRICTLY, type, TailType.NONE));
for (PsiType parent : processedTypes) {
result.add(createInfo(parent, ExpectedTypeInfo.TYPE_STRICTLY, parent, TailType.NONE));
}
//return Object if it is impossible to find common types
if (result.isEmpty() && !expectedTypes.isEmpty()) {
PsiClassType objectType = TypeUtils.getObjectType(statement);
result.add(createInfo(objectType, ExpectedTypeInfo.TYPE_STRICTLY, objectType, TailType.NONE));
}
return result;
}

View File

@@ -0,0 +1,22 @@
class Test {
String testSwitch() {
BaseInterface k = new BaseInterface.Record1();
int i2 = 0;
return switch (k<caret>)
{
case BaseInterface.Record1 record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
sealed interface BaseInterface permits BaseInterface.Record1, BaseInterface.Record2{
record Record1() implements BaseInterface {
}
record Record2() implements BaseInterface {
}
}
}

View File

@@ -0,0 +1,22 @@
class Test {
String testSwitch() {
BaseInterface k = new BaseInterface.Record1();
int i2 = 0;
return switch (<caret>)
{
case BaseInterface.Record1 record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
sealed interface BaseInterface permits BaseInterface.Record1, BaseInterface.Record2{
record Record1() implements BaseInterface {
}
record Record2() implements BaseInterface {
}
}
}

View File

@@ -0,0 +1,21 @@
class Test {
String testSwitch() {
BaseInterface<String> i = null;
BaseInterface<Character> j = null;
return switch (i<caret>) {
case BaseInterface.Record1<String> record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
sealed interface BaseInterface<T> permits BaseInterface.Record1, BaseInterface.Record2 {
record Record1<T>() implements BaseInterface<T> {
}
record Record2() implements BaseInterface<String> {
}
}
}

View File

@@ -0,0 +1,21 @@
class Test {
String testSwitch() {
BaseInterface<String> i = null;
BaseInterface<Character> j = null;
return switch (<caret>) {
case BaseInterface.Record1<String> record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
sealed interface BaseInterface<T> permits BaseInterface.Record1, BaseInterface.Record2 {
record Record1<T>() implements BaseInterface<T> {
}
record Record2() implements BaseInterface<String> {
}
}
}

View File

@@ -0,0 +1,22 @@
// "Create local variable 'i'" "true-preview"
class A {
String testPattern() {
BaseInterface<String> i;
return switch (i)
{
case BaseInterface.Record1<String> record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
}
sealed interface BaseInterface<T> permits BaseInterface.Record1, BaseInterface.Record2{
record Record1<T>() implements BaseInterface<T> {
}
record Record2() implements BaseInterface<String> {
}
}

View File

@@ -0,0 +1,21 @@
// "Create local variable 'i'" "true-preview"
class A {
String testPattern() {
return switch (i<caret>)
{
case BaseInterface.Record1<String> record1 -> "1";
case BaseInterface.Record2 record2 -> "2";
default -> "3";
};
}
}
sealed interface BaseInterface<T> permits BaseInterface.Record1, BaseInterface.Record2{
record Record1<T>() implements BaseInterface<T> {
}
record Record2() implements BaseInterface<String> {
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.java.codeInsight.completion;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
public class SmartType20CompletionTest extends LightFixtureCompletionTestCase {
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JAVA_20;
}
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/smartType/";
}
@Override
protected void complete() {
myItems = myFixture.complete(CompletionType.SMART);
}
public void testSwitchPattern() { doTest(); }
public void testSwitchPatternGeneric() { doTest(); }
private void doTest() {
configureByFile("/" + getTestName(false) + ".java");
if (myItems != null) {
final Lookup lookup = getLookup();
if (lookup != null) {
selectItem(lookup.getCurrentItem(), Lookup.NORMAL_SELECT_CHAR);
}
}
checkResultByFile("/" + getTestName(false) + "-out.java");
}
}