[java-highlighting] Provide quick-fix to replace 'var' with explicit type in language level smaller than Java 11

IDEA-256074

GitOrigin-RevId: 478ca1de890ec7c4346e4b2436c2b500c1489602
This commit is contained in:
Andrey.Cherkasov
2022-02-16 06:11:34 +00:00
committed by intellij-monorepo-bot
parent 5053b65a3f
commit 22e275ba54
13 changed files with 159 additions and 1 deletions
@@ -1,11 +1,17 @@
// Copyright 2000-2018 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-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.PriorityIntentionActionWrapper;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.psi.*;
import com.intellij.psi.PsiClassType.ClassResolveResult;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavaFutureKeywordUseFixProvider extends UnresolvedReferenceQuickFixProvider<PsiJavaCodeReferenceElement> {
@Override
@@ -14,6 +20,7 @@ public class JavaFutureKeywordUseFixProvider extends UnresolvedReferenceQuickFix
if (typeElement == null || typeElement.getFirstChild() != typeElement.getLastChild()) return;
PsiElement parent = typeElement.getParent();
if (PsiKeyword.VAR.equals(ref.getReferenceName())) {
registerSetVariableTypeFix(parent, registrar);
registerVarLanguageLevelFix(ref, parent, registrar);
}
if (PsiKeyword.RECORD.equals(ref.getReferenceName())) {
@@ -54,4 +61,42 @@ public class JavaFutureKeywordUseFixProvider extends UnresolvedReferenceQuickFix
public Class<PsiJavaCodeReferenceElement> getReferenceClass() {
return PsiJavaCodeReferenceElement.class;
}
private static void registerSetVariableTypeFix(PsiElement parent, @NotNull QuickFixActionRegistrar registrar) {
PsiVariable variable = ObjectUtils.tryCast(parent, PsiVariable.class);
if (variable == null) return;
PsiType type = inferType(variable);
if (type == null) return;
registrar.register(PriorityIntentionActionWrapper.highPriority(QuickFixFactory.getInstance().createSetVariableTypeFix(variable, type)));
}
private static @Nullable PsiType inferType(@NotNull PsiVariable variable) {
if (variable instanceof PsiParameter && variable.getParent() instanceof PsiForeachStatement) {
PsiForeachStatement foreach = (PsiForeachStatement)variable.getParent();
PsiExpression iteratedValue = foreach.getIteratedValue();
return iteratedValue != null ? JavaGenericsUtil.getCollectionItemType(iteratedValue) : null;
}
else if (variable instanceof PsiParameter) {
PsiParameterList parameterList = ObjectUtils.tryCast(variable.getParent(), PsiParameterList.class);
if (parameterList == null) return null;
PsiLambdaExpression lambda = ObjectUtils.tryCast(parameterList.getParent(), PsiLambdaExpression.class);
if (lambda == null) return null;
PsiType functionalInterfaceType = lambda.getFunctionalInterfaceType();
if (functionalInterfaceType == null) return null;
ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) return null;
PsiParameter[] parameters = interfaceMethod.getParameterList().getParameters();
int index = parameterList.getParameterIndex((PsiParameter)variable);
if (index >= parameters.length) return null;
return LambdaUtil.getSubstitutor(interfaceMethod, resolveResult).substitute(parameters[index].getType());
}
else if (variable instanceof PsiLocalVariable) {
PsiExpression initializer = variable.getInitializer();
if (initializer == null) return null;
PsiType type = initializer.getType();
return PsiTypesUtil.isDenotableType(type, variable) && !PsiType.VOID.equals(type) ? type : null;
}
return null;
}
}
@@ -0,0 +1,9 @@
// "Set variable type to 'String'" "true"
import java.util.List;
class Demo {
void test(List<String> list) {
for (String s : list) {
}
}
}
@@ -0,0 +1,9 @@
// "Set variable type to 'HashMap<String, List<String>>'" "true"
import java.util.HashMap;
import java.util.List;
class Demo {
void test() {
HashMap<String, List<String>> m = new HashMap<String, List<String>>();
}
}
@@ -0,0 +1,11 @@
// "Set variable type to 'FileInputStream'" "true"
import java.io.FileInputStream;
import java.io.FileNotFoundException;
class Demo {
void test() {
try (FileInputStream input = new FileInputStream("validation.txt")) {
} catch (FileNotFoundException e) {
}
}
}
@@ -0,0 +1,9 @@
// "Set variable type to 'String'" "true"
import java.util.List;
class Demo {
void test(List<String> list) {
for (var<caret> s : list) {
}
}
}
@@ -0,0 +1,6 @@
// "Set variable type to '<lambda expression>'" "false"
class Demo {
void test() {
var<caret> s = () -> {};
}
}
@@ -0,0 +1,8 @@
// "Set variable type to '<lambda parameter>'" "false"
class Demo {
void test() {
Object x = a -> {
var<caret> y = a;
};
}
}
@@ -0,0 +1,9 @@
// "Set variable type to 'HashMap<String, List<String>>'" "true"
import java.util.HashMap;
import java.util.List;
class Demo {
void test() {
var<caret> m = new HashMap<String, List<String>>();
}
}
@@ -0,0 +1,6 @@
// "Set variable type to '<method reference>'" "false"
class Demo {
void test() {
var<caret> s = this::foo;
}
}
@@ -0,0 +1,6 @@
// "Set variable type to 'null'" "false"
class Demo {
void test() {
var<caret> s = null;
}
}
@@ -0,0 +1,11 @@
// "Set variable type to 'FileInputStream'" "true"
import java.io.FileInputStream;
import java.io.FileNotFoundException;
class Demo {
void test() {
try (var<caret> input = new FileInputStream("validation.txt")) {
} catch (FileNotFoundException e) {
}
}
}
@@ -0,0 +1,8 @@
// "Set variable type to 'void'" "false"
public class Demo {
void test() {
var<caret> s = foo();
}
void foo() {}
}
@@ -0,0 +1,21 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class SetExplicitTypeTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/setExplicitType";
}
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return LightJavaCodeInsightFixtureTestCase.JAVA_9;
}
}