[java-intentions] PsiElementFactoryImpl.createVariableDeclarationStatement: use 'var' if non-denotable type is requested

Fixes IDEA-374601 Copy 'x' to effectively final variable breaks code when type is non-denotable

GitOrigin-RevId: 3b2d15f24b182ac3948ae3b5066c5e68c1c0bf5c
This commit is contained in:
Tagir Valeev
2025-06-17 18:17:52 +00:00
committed by intellij-monorepo-bot
parent 663e99a123
commit 05e9197580
4 changed files with 43 additions and 3 deletions
@@ -23,6 +23,7 @@ import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.IncorrectOperationException;
@@ -592,11 +593,15 @@ public final class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl impleme
throw new IncorrectOperationException("Cannot create variable with type \"null\".");
}
String text = "X " + name + (initializer != null ? " = x" : "") + ";";
String text = "var " + name + (initializer != null ? " = x" : "") + ";";
PsiDeclarationStatement statement = (PsiDeclarationStatement)createStatementFromText(text, context);
PsiVariable variable = (PsiVariable)statement.getDeclaredElements()[0];
replace(variable.getTypeElement(), createTypeElement(GenericsUtil.getVariableTypeByExpressionType(type)), text);
boolean generateVar = !PsiTypesUtil.isDenotableType(type, variable) && initializer != null &&
PsiUtil.isAvailable(JavaFeature.LVTI, initializer);
if (!generateVar) {
replace(variable.getTypeElement(), createTypeElement(GenericsUtil.getVariableTypeByExpressionType(type)), text);
}
boolean generateFinalLocals =
context != null && JavaFileCodeStyleFacade.forContext(context.getContainingFile()).isGenerateFinalLocals();
@@ -0,0 +1,18 @@
// "Copy 'x' to effectively final temp variable" "true-preview"
class Abc {
interface A{}
interface B{
void m();
}
void test(Object obj) {
var x = (A & B) obj;
if (Math.random() > 0.5) {
x = null;
}
var finalX = x;
Runnable r = () -> {
if (finalX != null) finalX.m();
};
}
}
@@ -0,0 +1,17 @@
// "Copy 'x' to effectively final temp variable" "true-preview"
class Abc {
interface A{}
interface B{
void m();
}
void test(Object obj) {
var x = (A & B) obj;
if (Math.random() > 0.5) {
x = null;
}
Runnable r = () -> {
if (<caret>x != null) x.m();
};
}
}
@@ -1,7 +1,7 @@
// "Replace method reference with lambda" "true-preview"
class Test {
{
Runnable runnable1 = new Runnable() {
var runnable1 = new Runnable() {
{
}