mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
[java-inspections] UseHashCodeMethodInspection: support reporting and replacing with Double.hashCode() when temp var is reused
GitOrigin-RevId: 8b6169bd90214b30d3d83acf7ebe49bc20b4f29d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a9e8828ed9
commit
45d3f80e5e
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.codeInspection;
|
||||
|
||||
import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
@@ -6,16 +6,17 @@ import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.DefUseUtil;
|
||||
import com.intellij.psi.util.PsiPrecedenceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@@ -48,20 +49,21 @@ public final class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInsp
|
||||
record HashCodeModel(@NotNull PsiExpression completeExpression,
|
||||
@NotNull PsiExpression argument,
|
||||
@Nullable PsiLocalVariable intermediateVariable,
|
||||
@Nullable PsiExpression definition,
|
||||
@NotNull String type) {
|
||||
@NotNull HashCodeModel tryReplaceDouble() {
|
||||
PsiLocalVariable local = ExpressionUtils.resolveLocalVariable(argument);
|
||||
if (local == null) return this;
|
||||
if (!(PsiUtil.skipParenthesizedExprDown(local.getInitializer()) instanceof PsiMethodCallExpression call)) return this;
|
||||
@Nullable PsiExpression expression = PsiUtil.skipParenthesizedExprDown(argument);
|
||||
if (!(expression instanceof PsiReferenceExpression referenceExpression)) return this;
|
||||
if (!(referenceExpression.resolve() instanceof PsiLocalVariable local)) return this;
|
||||
PsiExpression definition = PsiUtil.skipParenthesizedExprDown(DeclarationSearchUtils.findDefinition(referenceExpression, local));
|
||||
if (!(definition instanceof PsiMethodCallExpression call)) return this;
|
||||
if (!DOUBLE_TO_LONG_BITS.matches(call)) return this;
|
||||
if (!(local.getParent() instanceof PsiDeclarationStatement decl) || decl.getDeclaredElements().length != 1) return this;
|
||||
PsiElement nextDeclaration = PsiTreeUtil.skipWhitespacesAndCommentsForward(decl);
|
||||
if (!PsiTreeUtil.isAncestor(nextDeclaration, completeExpression, true)) return this;
|
||||
if (ContainerUtil.exists(VariableAccessUtils.getVariableReferences(local, PsiUtil.getVariableCodeBlock(local, null)),
|
||||
ref -> !PsiTreeUtil.isAncestor(completeExpression, ref, true))) {
|
||||
return this;
|
||||
}
|
||||
return new HashCodeModel(completeExpression, call.getArgumentList().getExpressions()[0], local, "Double");
|
||||
PsiStatement statement = PsiTreeUtil.getParentOfType(definition, PsiStatement.class);
|
||||
PsiElement nextStatement = PsiTreeUtil.skipWhitespacesAndCommentsForward(statement);
|
||||
if (!PsiTreeUtil.isAncestor(nextStatement, completeExpression, true)) return this;
|
||||
final PsiCodeBlock block = PsiTreeUtil.getParentOfType(local, PsiCodeBlock.class);
|
||||
if (block == null || DefUseUtil.getRefs(block, local, definition).length != 2) return this;
|
||||
return new HashCodeModel(completeExpression, call.getArgumentList().getExpressions()[0], local, definition, "Double");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +82,8 @@ public final class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInsp
|
||||
if (leftOperand == null || rightOperand == null) return null;
|
||||
if (!PsiTypes.longType().equals(PsiPrimitiveType.getOptionallyUnboxedType(leftOperand.getType()))) return null;
|
||||
|
||||
if (isXorShift(leftOperand, rightOperand)) return new HashCodeModel(cast, leftOperand, null, "Long").tryReplaceDouble();
|
||||
if (isXorShift(rightOperand, leftOperand)) return new HashCodeModel(cast, rightOperand, null, "Long").tryReplaceDouble();
|
||||
if (isXorShift(leftOperand, rightOperand)) return new HashCodeModel(cast, leftOperand, null, null, "Long").tryReplaceDouble();
|
||||
if (isXorShift(rightOperand, leftOperand)) return new HashCodeModel(cast, rightOperand, null, null, "Long").tryReplaceDouble();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -119,19 +121,21 @@ public final class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInsp
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement startElement, @NotNull ModPsiUpdater updater) {
|
||||
PsiTypeCastExpression element = (PsiTypeCastExpression)startElement;
|
||||
HashCodeModel model = getHashCodeModel(element);
|
||||
HashCodeModel model = getHashCodeModel((PsiTypeCastExpression)startElement);
|
||||
if (model == null) return;
|
||||
PsiExpression argument = model.argument();
|
||||
PsiType type = argument.getType();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String call = type instanceof PsiPrimitiveType
|
||||
String call = argument.getType() instanceof PsiPrimitiveType
|
||||
? "java.lang." + model.type() + ".hashCode(" + ct.text(argument) + ")"
|
||||
: ct.text(argument, PsiPrecedenceUtil.METHOD_CALL_PRECEDENCE) + ".hashCode()";
|
||||
if (model.intermediateVariable() != null) {
|
||||
ct.delete(model.intermediateVariable());
|
||||
PsiLocalVariable local = model.intermediateVariable;
|
||||
if (local != null && model.definition != null) {
|
||||
PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(model.definition, PsiExpressionStatement.class);
|
||||
if (expressionStatement != null) ct.delete(expressionStatement);
|
||||
List<PsiReferenceExpression> references = VariableAccessUtils.getVariableReferences(local);
|
||||
if (references.size() == 2) ct.delete(local);
|
||||
}
|
||||
ct.replaceAndRestoreComments(element, call);
|
||||
ct.replaceAndRestoreComments(startElement, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d1;
|
||||
private double d2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
result = 31 * result + Double.hashCode(d1);
|
||||
temp = Double.doubleToLongBits(d2);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d1;
|
||||
private double d2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
temp = Double.doubleToLongBits(d1);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
result = 31 * result + Double.hashCode(d2);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
result = 31 * result + Double.hashCode(d);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d1;
|
||||
private double d2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
temp = Double.doubleToLongBits(d1);
|
||||
result = 31 * result + (int)(temp ^<caret> (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(d2);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d1;
|
||||
private double d2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
temp = Double.doubleToLongBits(d1);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(d2);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32)<caret>);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
|
||||
class X {
|
||||
private String s;
|
||||
private double d;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = s != null ? s.hashCode() : 0;
|
||||
temp = Double.doubleToLongBits(d);
|
||||
result = 31 * result + (int)<caret>(temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user