mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 22:09:38 +07:00
[java-refactoring] TypeMigration: migration rules for optional
GitOrigin-RevId: 5f7c1413476c7aea1b6282e86426e7ccf414c94b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4e8623cb77
commit
86d9d3a43e
@@ -4,6 +4,7 @@
|
||||
implementation="com.intellij.refactoring.typeMigration.TypeMigrationVariableTypeFixProvider"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.ListArrayConversionRule"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.AtomicConversionRule"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.OptionalConversionRule"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.BoxingTypeConversionRule"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.ElementToArrayConversionRule"/>
|
||||
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.ThreadLocalConversionRule"/>
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.refactoring.typeMigration.rules;
|
||||
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInspection.dataFlow.NullabilityUtil;
|
||||
import com.intellij.codeInspection.util.OptionalUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
|
||||
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
|
||||
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
|
||||
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import com.siyeh.ig.psiutils.VariableNameGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class OptionalConversionRule extends TypeConversionRule {
|
||||
private static final Map<String, String> GET_METHODS = Map.of(
|
||||
CommonClassNames.JAVA_UTIL_OPTIONAL, "orElse(null)",
|
||||
"java.util.OptionalInt", "getAsInt()",
|
||||
"java.util.OptionalLong", "getAsLong()",
|
||||
"java.util.OptionalDouble", "getAsDouble()");
|
||||
|
||||
@Override
|
||||
public @Nullable TypeConversionDescriptorBase findConversion(PsiType from,
|
||||
PsiType to,
|
||||
PsiMember member,
|
||||
PsiExpression context,
|
||||
TypeMigrationLabeler labeler) {
|
||||
PsiType optionalElementType = OptionalUtil.getOptionalElementType(to);
|
||||
if (optionalElementType == null) return null;
|
||||
if (!from.equals(optionalElementType)) {
|
||||
return null;
|
||||
}
|
||||
PsiClass optionalClass = PsiUtil.resolveClassInClassTypeOnly(to);
|
||||
if (optionalClass == null) return null;
|
||||
String qualifiedName = optionalClass.getQualifiedName();
|
||||
if (qualifiedName == null) return null;
|
||||
PsiElement parent = context.getParent();
|
||||
TypeMigrationUsageInfo root = labeler.getCurrentRoot();
|
||||
PsiElement element = root == null ? null : root.getElement();
|
||||
if (element != null && context instanceof PsiReferenceExpression ref) {
|
||||
PsiExpression qualifier = ref.getQualifierExpression();
|
||||
if (qualifier instanceof PsiReferenceExpression qualifierRef && qualifierRef.isReferenceTo(element)) {
|
||||
return new TypeConversionDescriptor("$val$", "$val$.get()", qualifier);
|
||||
}
|
||||
}
|
||||
if (element != null && context instanceof PsiReferenceExpression ref && ref.isReferenceTo(element)) {
|
||||
if (parent instanceof PsiBinaryExpression binOp) {
|
||||
IElementType tokenType = binOp.getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.EQEQ) || tokenType.equals(JavaTokenType.NE)) {
|
||||
PsiExpression lOp = binOp.getLOperand();
|
||||
PsiExpression rOp = binOp.getROperand();
|
||||
if (rOp != null) {
|
||||
if (ExpressionUtils.isNullLiteral(rOp)) {
|
||||
return new TypeConversionDescriptor("$val$" + binOp.getOperationSign().getText() + rOp.getText(),
|
||||
tokenType.equals(JavaTokenType.EQEQ) ? "$val$.isEmpty()" : "$val$.isPresent()", binOp);
|
||||
}
|
||||
if (ExpressionUtils.isNullLiteral(lOp)) {
|
||||
return new TypeConversionDescriptor(lOp.getText() + binOp.getOperationSign().getText() + "$val$",
|
||||
tokenType.equals(JavaTokenType.EQEQ) ? "$val$.isEmpty()" : "$val$.isPresent()", binOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new TypeConversionDescriptor("$val$", "$val$." + GET_METHODS.get(qualifiedName), context);
|
||||
}
|
||||
else if (parent instanceof PsiAssignmentExpression assignment && assignment.getRExpression() == context
|
||||
&& element != null &&
|
||||
assignment.getLExpression() instanceof PsiReferenceExpression ref && ref.isReferenceTo(element) &&
|
||||
assignment.getOperationTokenType().equals(JavaTokenType.PLUSEQ) && TypeUtils.isJavaLangString(optionalElementType)) {
|
||||
String varName = new VariableNameGenerator(context, VariableKind.PARAMETER).byName(ref.getText()).byType(optionalElementType).generate(true);
|
||||
return new TypeConversionDescriptor("$val$+=$operand$", "$val$=$val$.map(" + varName + "->" + varName + "+$operand$)", assignment);
|
||||
}
|
||||
else if (context instanceof PsiAssignmentExpression assignment) {
|
||||
String compoundOp = assignment.getOperationSign().getText();
|
||||
String op = compoundOp.substring(0, compoundOp.length() - 1);
|
||||
if (!op.isEmpty()) {
|
||||
return new TypeConversionDescriptor(
|
||||
"$val$" + compoundOp + "$operand$",
|
||||
"$val$=" + qualifiedName + ".of($val$." + GET_METHODS.get(qualifiedName) + op + "$operand$)", context);
|
||||
}
|
||||
}
|
||||
else if (context instanceof PsiUnaryExpression unary &&
|
||||
(context instanceof PsiPrefixExpression || ExpressionUtils.isVoidContext(unary))) {
|
||||
IElementType tokenType = unary.getOperationTokenType();
|
||||
String op = tokenType.equals(JavaTokenType.PLUSPLUS) ? "+" :
|
||||
tokenType.equals(JavaTokenType.MINUSMINUS) ? "-" : null;
|
||||
if (op != null) {
|
||||
String orig = "$val$";
|
||||
if (context instanceof PsiPrefixExpression) {
|
||||
orig = op + op + orig;
|
||||
}
|
||||
else {
|
||||
orig = orig + op + op;
|
||||
}
|
||||
return new TypeConversionDescriptor(orig,
|
||||
"$val$=" + qualifiedName + ".of($val$." + GET_METHODS.get(qualifiedName) + op + "1)", context);
|
||||
}
|
||||
}
|
||||
if (ExpressionUtils.isVoidContext(context)) {
|
||||
return new TypeConversionDescriptor("$val$", "$val$", context);
|
||||
}
|
||||
return wrap(context, qualifiedName);
|
||||
}
|
||||
|
||||
private static @NotNull TypeConversionDescriptor wrap(PsiExpression context, String qualifiedName) {
|
||||
if (ExpressionUtils.isNullLiteral(context)) {
|
||||
return new TypeConversionDescriptor("$val$", qualifiedName + ".empty()", context);
|
||||
}
|
||||
if (NullabilityUtil.getExpressionNullability(context, true) == Nullability.NOT_NULL) {
|
||||
return new TypeConversionDescriptor("$val$", qualifiedName + ".of($val$)", context);
|
||||
}
|
||||
return new TypeConversionDescriptor("$val$", qualifiedName + ".ofNullable($val$)", context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConvertNullInitializer(PsiType from, PsiType to, PsiExpression context) {
|
||||
return from.equals(OptionalUtil.getOptionalElementType(to));
|
||||
}
|
||||
}
|
||||
@@ -867,6 +867,14 @@ public class TypeMigrationTest extends TypeMigrationTestBase {
|
||||
public void testGetterToBoolean() {
|
||||
doTestFieldType("fooMigrateName", PsiTypes.booleanType());
|
||||
}
|
||||
|
||||
public void testInt2OptionalInt() {
|
||||
doTestFirstParamType("test", myFactory.createTypeFromText("java.util.OptionalInt", null));
|
||||
}
|
||||
|
||||
public void testString2OptionalString() {
|
||||
doTestFirstParamType("testString", myFactory.createTypeFromText("java.util.Optional<java.lang.String>", null));
|
||||
}
|
||||
|
||||
public void testGetterToBoolean2() {
|
||||
doTestFieldType("fooDontMigrateName", PsiTypes.booleanType());
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
Types:
|
||||
PsiAssignmentExpression:x += 2 : java.util.OptionalInt
|
||||
PsiParameter:x : java.util.OptionalInt
|
||||
PsiPostfixExpression:x++ : java.util.OptionalInt
|
||||
PsiReferenceExpression:x : java.util.OptionalInt
|
||||
PsiReferenceExpression:x : java.util.OptionalInt
|
||||
PsiReferenceExpression:x : java.util.OptionalInt
|
||||
PsiReferenceExpression:x : java.util.OptionalInt
|
||||
PsiReferenceExpression:x : java.util.OptionalInt
|
||||
|
||||
Conversions:
|
||||
10 -> java.util.OptionalInt.of($val$) $val$ 10
|
||||
124 -> java.util.OptionalInt.of($val$) $val$ 124
|
||||
x += 2 -> $val$=java.util.OptionalInt.of($val$.getAsInt()+$operand$) $val$+=$operand$ x += 2
|
||||
x -> $val$.getAsInt() $val$ x
|
||||
x -> $val$.getAsInt() $val$ x
|
||||
x++ -> $val$=java.util.OptionalInt.of($val$.getAsInt()+1) $val$++ x++
|
||||
|
||||
New expression type changes:
|
||||
Fails:
|
||||
@@ -0,0 +1,15 @@
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
test(OptionalInt.of(124));
|
||||
}
|
||||
|
||||
static void test(OptionalInt x) {
|
||||
x = OptionalInt.of(10);
|
||||
x = OptionalInt.of(x.getAsInt() + 1);
|
||||
x = OptionalInt.of(x.getAsInt() + 2);
|
||||
System.out.println(x.getAsInt());
|
||||
System.out.println(x.getAsInt() + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
test(124);
|
||||
}
|
||||
|
||||
static void test(int x) {
|
||||
x = 10;
|
||||
x++;
|
||||
x += 2;
|
||||
System.out.println(x);
|
||||
System.out.println(x + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
Types:
|
||||
PsiAssignmentExpression:s += null : java.util.Optional<java.lang.String>
|
||||
PsiMethodCallExpression:s.trim() : java.lang.String
|
||||
PsiParameter:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
PsiReferenceExpression:s : java.util.Optional<java.lang.String>
|
||||
|
||||
Conversions:
|
||||
"World " -> java.util.Optional.of($val$) $val$ "World "
|
||||
"hello" -> $val$=$val$.map(string->string+$operand$) $val$+=$operand$ s += "hello"
|
||||
null -> java.util.Optional.empty() $val$ null
|
||||
null -> java.util.Optional.empty() $val$ null
|
||||
s += null -> $val$=java.util.Optional.of($val$.orElse(null)+$operand$) $val$+=$operand$ s += null
|
||||
s -> $val$.isEmpty() $val$==null s == null
|
||||
s -> $val$.isPresent() $val$!=null s != null
|
||||
s -> $val$.orElse(null) $val$ s
|
||||
s.trim() -> $val$.get() $val$ s
|
||||
|
||||
New expression type changes:
|
||||
Fails:
|
||||
@@ -0,0 +1,21 @@
|
||||
import java.util.Optional;
|
||||
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
testString(Optional.of("World "));
|
||||
testString(Optional.empty());
|
||||
}
|
||||
|
||||
static void testString(Optional<String> s) {
|
||||
if (s.isEmpty()) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
if (s.isPresent()) {
|
||||
s = s.map(string -> string + "hello");
|
||||
System.out.println(s.orElse(null));
|
||||
}
|
||||
s = Optional.of(s.orElse(null) + null);
|
||||
System.out.println(s.get().trim());
|
||||
s = Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
testString("World ");
|
||||
testString(null);
|
||||
}
|
||||
|
||||
static void testString(String s) {
|
||||
if (s == null) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
if (s != null) {
|
||||
s += "hello";
|
||||
System.out.println(s);
|
||||
}
|
||||
s += null;
|
||||
System.out.println(s.trim());
|
||||
s = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user