[java-inspection] IDEA-347245 Do not report 'Redundant local variable' when inlining would require a cast

GitOrigin-RevId: 3735bc589a2998c7117d9c9bee62831945869ede
This commit is contained in:
Tagir Valeev
2024-03-16 12:19:14 +00:00
committed by intellij-monorepo-bot
parent f6672246e7
commit b3efd6516e
6 changed files with 36 additions and 37 deletions
@@ -469,14 +469,13 @@ public final class InstanceOfUtils {
PsiElement block = PsiUtil.getVariableCodeBlock(variable, null);
if (block == null) return false;
for (PsiReferenceExpression reference : VariableAccessUtils.getVariableReferences(variable, block)) {
if (!isVariableTypeChangeSafeForReference(cast, castType, reference)) return false;
if (PsiTreeUtil.isAncestor(cast, reference, true)) continue;
if (!isVariableTypeChangeSafeForReference(castType, reference)) return false;
}
return true;
}
private static boolean isVariableTypeChangeSafeForReference(@NotNull PsiTypeCastExpression cast,
@NotNull PsiType targetType,
@NotNull PsiReferenceExpression reference) {
static boolean isVariableTypeChangeSafeForReference(@NotNull PsiType targetType, @NotNull PsiReferenceExpression reference) {
PsiElement parent = PsiUtil.skipParenthesizedExprUp(reference.getParent());
if (PsiUtil.isAccessedForWriting(reference)) {
PsiAssignmentExpression assignmentExpression = tryCast(parent, PsiAssignmentExpression.class);
@@ -496,11 +495,11 @@ public final class InstanceOfUtils {
// Could be always false instanceof which will become compilation error after fix
return TypeConversionUtil.areTypesConvertible(targetType, checkType);
}
if (parent instanceof PsiTypeCastExpression parentCast && parent != cast) {
if (parent instanceof PsiTypeCastExpression parentCast) {
PsiTypeElement castTypeElement = parentCast.getCastType();
if (castTypeElement == null) return false;
PsiType castType = castTypeElement.getType();
// Another cast could become invalid due to this change
// Another replacement could become invalid due to this change
return TypeConversionUtil.areTypesConvertible(targetType, castType);
}
// Some method call can be mis-resolved after update, check this
@@ -513,16 +512,18 @@ public final class InstanceOfUtils {
PsiTreeUtil.releaseMark(reference, mark);
PsiElement refCopy = PsiTreeUtil.releaseMark(callCopy, mark);
if (refCopy == null) return false;
PsiTypeCastExpression insertedCast = (PsiTypeCastExpression)refCopy.replace(cast);
Objects.requireNonNull(insertedCast.getCastType())
.replace(JavaPsiFacade.getElementFactory(call.getProject()).createTypeElement(targetType));
PsiElementFactory factory = JavaPsiFacade.getElementFactory(call.getProject());
PsiTypeCastExpression insertedCast = (PsiTypeCastExpression)refCopy.replace(
factory.createExpressionFromText("(a)"+reference.getReferenceName(), refCopy));
Objects.requireNonNull(insertedCast.getCastType()).replace(factory.createTypeElement(targetType));
return callCopy.resolveMethod() == method;
}
if (parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
if (method == null) return false;
// private method cannot be called on a subtype qualifier
return !method.hasModifierProperty(PsiModifier.PRIVATE);
if (parent instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)parent).resolve();
// private member cannot be accessed on a subtype qualifier
if (resolve instanceof PsiMember member && member.hasModifierProperty(PsiModifier.PRIVATE)) {
return false;
}
}
return true;
}
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
@@ -493,8 +492,7 @@ public final class VariableAccessUtils {
final PsiType variableType = variable.getType();
final PsiType initializationType = initialization.getType();
final boolean sameType = Comparing.equal(variableType, initializationType);
for (PsiReference ref : ReferencesSearch.search(variable, new LocalSearchScope(containingScope))) {
final PsiElement refElement = ref.getElement();
for (PsiReferenceExpression refElement : getVariableReferences(variable)) {
if (finalVariableIntroduction || canCaptureThis) {
final PsiElement element = PsiTreeUtil.getParentOfType(refElement, PsiClass.class, PsiLambdaExpression.class);
if (element != null && PsiTreeUtil.isAncestor(containingScope, element, true)) {
@@ -506,15 +504,8 @@ public final class VariableAccessUtils {
return false;
}
if (!sameType) {
final PsiElement parent = refElement.getParent();
if (parent instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)parent).resolve();
if (resolve instanceof PsiMember &&
((PsiMember)resolve).hasModifierProperty(PsiModifier.PRIVATE)) {
return false;
}
}
if (!sameType && !InstanceOfUtils.isVariableTypeChangeSafeForReference(initializationType, refElement)) {
return false;
}
}
@@ -1,10 +0,0 @@
class Base {
{
Descendant descendant = new Descendant();
consume((Base) descendant);
}
private static void consume(Base value) {}
private static void consume(Descendant value) {}
}
class Descendant extends Base {}
@@ -0,0 +1,15 @@
class Scratch {
public static void main(String... arguments) {
String <warning descr="Local variable 's' is redundant">s</warning> = arguments[0];
Object o = s;
use(o);
}
private static void use(Object objectionable) {
System.out.println("Objection!");
}
private static void use(String s) {
System.out.println("Strings are like ropes. Threads are like fibers");
}
}
@@ -11,6 +11,8 @@ public class UnnecessaryLocalVariableInspectionTest extends LightJavaInspectionT
}
public void testUnnecessaryLocalVariable() { doTest(); }
public void testCastNecessary() { doTest(); }
public void testTree() { doTest(); }
@@ -19,6 +19,6 @@ public class InlineVariableFixTest extends IGQuickFixesTestCase {
public void testSingleResourceVar() { doTest(); }
public void testCastNeeded() { doTest(); }
public void testArrayInitializer() { doTest(); }
public void testCastForOverloads() { doTest(); }
public void testCastForOverloads() { assertQuickfixNotAvailable(); }
public void testComment() { doTest(); }
}