Java: add cast when necessary in more places (IJ-CR-107436, IDEA-248610)

GitOrigin-RevId: 97431697ea4a59bb68a0d2309ad70d6bfe6e674f
This commit is contained in:
Bas Leijdekkers
2023-05-16 00:11:57 +02:00
committed by intellij-monorepo-bot
parent 8804e740be
commit e91500e822
7 changed files with 85 additions and 42 deletions

View File

@@ -1083,7 +1083,7 @@ public class TypeMigrationLabeler {
return myRootsTree; return myRootsTree;
} }
TypeMigrationUsageInfo getCurrentRoot() { public TypeMigrationUsageInfo getCurrentRoot() {
return myCurrentRoot; return myCurrentRoot;
} }

View File

@@ -22,15 +22,14 @@ import java.util.List;
public class ThreadLocalConversionRule extends TypeConversionRule { public class ThreadLocalConversionRule extends TypeConversionRule {
private static final Logger LOG = Logger.getInstance(ThreadLocalConversionRule.class); private static final Logger LOG = Logger.getInstance(ThreadLocalConversionRule.class);
@Override @Override
public TypeConversionDescriptorBase findConversion(PsiType from, public TypeConversionDescriptorBase findConversion(PsiType from,
PsiType to, PsiType to,
PsiMember member, PsiMember member,
PsiExpression context, PsiExpression context,
TypeMigrationLabeler labeler) { TypeMigrationLabeler labeler) {
if (to instanceof PsiClassType && isThreadLocalTypeMigration(from, (PsiClassType)to, context)) { if (to instanceof PsiClassType toClassType && isThreadLocalTypeMigration(from, toClassType, context)) {
return findDirectConversion(context, to, from, labeler); return findDirectConversion(context, toClassType, from, labeler);
} }
return null; return null;
} }
@@ -64,13 +63,17 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
} }
@Nullable @Nullable
private static TypeConversionDescriptor findDirectConversion(PsiElement context, PsiType to, PsiType from, TypeMigrationLabeler labeler) { private static TypeConversionDescriptor findDirectConversion(PsiElement context,
final PsiClass toTypeClass = PsiUtil.resolveClassInType(to); PsiClassType to,
PsiType from,
TypeMigrationLabeler labeler) {
final PsiClass toTypeClass = to.resolve();
LOG.assertTrue(toTypeClass != null); LOG.assertTrue(toTypeClass != null);
final PsiElement parent = context.getParent(); final PsiElement parent = context.getParent();
if (parent instanceof PsiVariable && ((PsiVariable)parent).getInitializer() == context) { if (parent.equals(labeler.getCurrentRoot().getElement()) && ((PsiVariable)parent).getInitializer() == context) {
return wrapWithNewExpression(to, from, (PsiExpression)context);
return wrapWithNewExpression(from, to, (PsiExpression)context);
} }
if (context instanceof PsiArrayAccessExpression) { if (context instanceof PsiArrayAccessExpression) {
return new TypeConversionDescriptor("$qualifier$[$val$]", "$qualifier$.get()[$val$]"); return new TypeConversionDescriptor("$qualifier$[$val$]", "$qualifier$.get()[$val$]");
@@ -78,8 +81,10 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
if (parent instanceof PsiAssignmentExpression) { if (parent instanceof PsiAssignmentExpression) {
final IElementType operationSign = ((PsiAssignmentExpression)parent).getOperationTokenType(); final IElementType operationSign = ((PsiAssignmentExpression)parent).getOperationTokenType();
if (operationSign == JavaTokenType.EQ) { if (operationSign == JavaTokenType.EQ) {
boolean rightInfected = ((PsiAssignmentExpression)parent).getLExpression() == context; final boolean rightInfected = ((PsiAssignmentExpression)parent).getLExpression() == context;
String replacement = rightInfected ? "$qualifier$ = $val$.get()" : "$qualifier$.set(" + toBoxed("$val$", from, context) + ")"; final String replacement = rightInfected
? "$qualifier$ = $val$.get()"
: "$qualifier$.set(" + coerceType("$val$", from, to, context) + ")";
return new TypeConversionDescriptor("$qualifier$ = $val$", replacement, (PsiAssignmentExpression)parent); return new TypeConversionDescriptor("$qualifier$ = $val$", replacement, (PsiAssignmentExpression)parent);
} }
} }
@@ -135,11 +140,11 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
if (lExpression instanceof PsiReferenceExpression) { if (lExpression instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReferenceExpression)lExpression).resolve(); final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
if (element instanceof PsiVariable && ((PsiVariable)element).hasModifierProperty(PsiModifier.FINAL)) { if (element instanceof PsiVariable && ((PsiVariable)element).hasModifierProperty(PsiModifier.FINAL)) {
return wrapWithNewExpression(to, from, ((PsiAssignmentExpression)context).getRExpression()); return wrapWithNewExpression(from, to, ((PsiAssignmentExpression)context).getRExpression());
} }
} }
return new TypeConversionDescriptor("$qualifier$ = $val$", return new TypeConversionDescriptor("$qualifier$ = $val$",
"$qualifier$.set(" + toBoxed("$val$", from, context) + ")"); "$qualifier$.set(" + coerceType("$val$", from, to, context) + ")");
} }
else { else {
final PsiExpression rExpression = assignmentExpression.getRExpression(); final PsiExpression rExpression = assignmentExpression.getRExpression();
@@ -155,39 +160,32 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
return null; return null;
} }
private static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, PsiExpression initializer) { private static TypeConversionDescriptor wrapWithNewExpression(PsiType from, PsiClassType to, PsiExpression initializer) {
List<PsiVariable> toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer); List<PsiVariable> toMakeFinal = TypeConversionRuleUtil.getVariablesToMakeFinal(initializer);
if (toMakeFinal == null) return null; if (toMakeFinal == null) return null;
return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$", return new WrappingWithInnerClassOrLambdaDescriptor("$qualifier$",
createThreadLocalInitializerReplacement(to, from, initializer), createThreadLocalInitializerReplacement(from, to, initializer),
initializer, initializer,
toMakeFinal); toMakeFinal);
} }
private static @NonNls String createThreadLocalInitializerReplacement(PsiType to, PsiType from, PsiElement context) { private static @NonNls String createThreadLocalInitializerReplacement(PsiType from, PsiClassType to, PsiElement context) {
if (PsiUtil.isLanguageLevel8OrHigher(context)) { if (PsiUtil.isLanguageLevel8OrHigher(context)) {
if (from instanceof PsiPrimitiveType) { return "java.lang.ThreadLocal.withInitial(() -> " + coerceType("$qualifier$", from, to, context) + ")";
PsiType parameterType = ((PsiClassType)to).getParameters()[0];
PsiPrimitiveType unboxed = PsiPrimitiveType.getUnboxedType(parameterType);
if (unboxed != null && !from.equals(unboxed)) {
return "java.lang.ThreadLocal.withInitial(() -> (" + unboxed.getCanonicalText() + ")$qualifier$)";
}
}
return "java.lang.ThreadLocal.withInitial(() -> $qualifier$)";
} }
final String boxedTypeName = final StringBuilder result = new StringBuilder("new ");
from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText(); result.append(to.getCanonicalText()).append("() {\n");
return (""" if (PsiUtil.isLanguageLevel5OrHigher(context)) {
new %s() { result.append(" @java.lang.Override\n");
@Override }
protected %s initialValue() { result.append(" protected ")
return %s; .append(PsiUtil.isLanguageLevel5OrHigher(context) ? to.getParameters()[0].getCanonicalText() : "java.lang.Object")
} .append(" initialValue() {\n")
}""").formatted(to.getCanonicalText(), .append(" return ")
boxedTypeName, .append(coerceType("$qualifier$", from, to, context)).append(";\n")
from instanceof PsiPrimitiveType && !PsiUtil.isLanguageLevel5OrHigher(context) .append(" }\n")
? "new " + ((PsiPrimitiveType)from).getBoxedTypeName() + "($qualifier$)" .append("}");
: "$qualifier$"); return result.toString();
} }
private static @NonNls String toPrimitive(@NonNls String replaceByArg, PsiType from, PsiElement context) { private static @NonNls String toPrimitive(@NonNls String replaceByArg, PsiType from, PsiElement context) {
@@ -199,8 +197,16 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
: "((" + from.getCanonicalText() + ")" + replaceByArg + ")"; : "((" + from.getCanonicalText() + ")" + replaceByArg + ")";
} }
private static @NonNls String toBoxed(@NonNls String replaceByArg, PsiType from, PsiElement context) { private static @NonNls String coerceType(@NonNls String replaceByArg, PsiType from, PsiClassType to, PsiElement context) {
if (PsiUtil.isLanguageLevel5OrHigher(context)) { if (PsiUtil.isLanguageLevel5OrHigher(context)) {
if (from instanceof PsiPrimitiveType) {
final PsiPrimitiveType unboxed = PsiPrimitiveType.getUnboxedType(to.getParameters()[0]);
if (unboxed != null && !from.equals(unboxed)) {
return context instanceof PsiLiteralExpression && PsiTypes.longType().equals(unboxed)
? replaceByArg + "L"
: "(" + unboxed.getCanonicalText() + ")" + replaceByArg;
}
}
return replaceByArg; return replaceByArg;
} }
return from instanceof PsiPrimitiveType return from instanceof PsiPrimitiveType
@@ -209,7 +215,7 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
} }
private static @NonNls String getBoxedWrapper(PsiType from, private static @NonNls String getBoxedWrapper(PsiType from,
PsiType to, PsiClassType to,
@NotNull @NonNls String arg, @NotNull @NonNls String arg,
TypeMigrationLabeler labeler, TypeMigrationLabeler labeler,
PsiElement context, PsiElement context,
@@ -227,14 +233,14 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
final PsiType exprType = labeler.getTypeEvaluator().evaluateType( final PsiType exprType = labeler.getTypeEvaluator().evaluateType(
JavaPsiFacade.getElementFactory(threadLocalClass.getProject()).createExpressionFromText(tryType, context)); JavaPsiFacade.getElementFactory(threadLocalClass.getProject()).createExpressionFromText(tryType, context));
if (exprType != null && unboxedInitialType.isAssignableFrom(exprType)) { if (exprType != null && unboxedInitialType.isAssignableFrom(exprType)) {
return toBoxed(arg, from, context); return coerceType(arg, from, to, context);
} }
} }
return "new " + initial.getCanonicalText() + "((" + unboxedInitialType.getCanonicalText() + ")(" + arg + "))"; return "new " + initial.getCanonicalText() + "((" + unboxedInitialType.getCanonicalText() + ")(" + arg + "))";
} }
} }
} }
return toBoxed(arg, from, context); return coerceType(arg, from, to, context);
} }
private static final class WrappingWithInnerClassOrLambdaDescriptor extends ArrayInitializerAwareConversionDescriptor { private static final class WrappingWithInnerClassOrLambdaDescriptor extends ArrayInitializerAwareConversionDescriptor {

View File

@@ -1,4 +1,10 @@
// "Convert to 'ThreadLocal'" "true" // "Convert to 'ThreadLocal'" "true"
class T { class T {
private static final ThreadLocal<Long> l = ThreadLocal.withInitial(() -> (long) 1); // choose "Convert to ThreadLocal" intention private static final ThreadLocal<Long> l = ThreadLocal.withInitial(() -> 1L); // choose "Convert to ThreadLocal" intention
static {
int i = 1;
l.set((long) i);
long z = l.get();
}
} }

View File

@@ -1,4 +1,10 @@
// "Convert to 'ThreadLocal'" "true" // "Convert to 'ThreadLocal'" "true"
class T { class T {
private static final long <caret>l = 1; // choose "Convert to ThreadLocal" intention private static final long <caret>l = 1; // choose "Convert to ThreadLocal" intention
static {
int i = 1;
l = i;
long z = l;
}
} }

View File

@@ -0,0 +1,15 @@
// "Convert to 'ThreadLocal'" "true"
class T {
private static final ThreadLocal<Long> l = new ThreadLocal<Long>() {
@Override
protected Long initialValue() {
return 1L;
}
};
static {
int i = 1;
l.set((long) i);
long z = l.get();
}
}

View File

@@ -0,0 +1,10 @@
// "Convert to 'ThreadLocal'" "true"
class T {
private static final long <caret>l = 1;
static {
int i = 1;
l = i;
long z = l;
}
}

View File

@@ -5,7 +5,7 @@ PsiReferenceExpression:myS : java.lang.ThreadLocal<java.lang.String>
Conversions: Conversions:
"" -> new java.lang.ThreadLocal<java.lang.String>() { "" -> new java.lang.ThreadLocal<java.lang.String>() {
@Override @java.lang.Override
protected java.lang.String initialValue() { protected java.lang.String initialValue() {
return $qualifier$; return $qualifier$;
} }