[java-inspections] IDEA-338114 Suggest to replace (int)(v ^ (v >>> 32)) with a Long.hashCode(v)

GitOrigin-RevId: 2d9591f9d9a558c4a61ac226556ef9f551d6e0c2
This commit is contained in:
Tagir Valeev
2023-11-14 16:45:50 +01:00
committed by intellij-monorepo-bot
parent 6da9127fbb
commit 2770254f2f
9 changed files with 174 additions and 0 deletions

View File

@@ -663,3 +663,5 @@ intention.family.name.move.members.into.class=Move members into class
chooser.popup.title.select.class.to.move.members.to=Select Target Class
intention.family.name.move.members.to=Move members to {0}
action.StatisticCollectorAction.text=Collect Documentation Data
inspection.message.can.be.replaced.with.long.hashcode=Can be replaced with 'Long.hashCode()'
inspection.name.can.be.replaced.with.long.hashcode='Long.hashCode()' can be used

View File

@@ -0,0 +1,87 @@
// Copyright 2000-2023 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;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR;
return new JavaElementVisitor() {
@Override
public void visitTypeCastExpression(@NotNull PsiTypeCastExpression expression) {
if (getHashCodeOperand(expression) != null) {
holder.registerProblem(expression, JavaAnalysisBundle.message("inspection.message.can.be.replaced.with.long.hashcode"),
new ReplaceWithLongHashCodeFix());
}
}
};
}
private static @Nullable PsiExpression getHashCodeOperand(PsiTypeCastExpression typeCastExpression) {
if (typeCastExpression == null) return null;
if (!PsiTypes.intType().equals(typeCastExpression.getType())) return null;
PsiExpression operand = PsiUtil.skipParenthesizedExprDown(typeCastExpression.getOperand());
if (!(operand instanceof PsiBinaryExpression binaryExpression)) return null;
PsiJavaToken operationSign = binaryExpression.getOperationSign();
if (operationSign.getTokenType() != JavaTokenType.XOR) return null;
PsiExpression leftOperand = PsiUtil.skipParenthesizedExprDown(binaryExpression.getLOperand());
PsiExpression rightOperand = PsiUtil.skipParenthesizedExprDown(binaryExpression.getROperand());
if (leftOperand == null || rightOperand == null) return null;
if (!PsiTypes.longType().equals(leftOperand.getType())) return null;
if (isXorShift(leftOperand, rightOperand)) return leftOperand;
if (isXorShift(rightOperand, leftOperand)) return rightOperand;
return null;
}
private static boolean isXorShift(@NotNull PsiExpression leftOperand, @NotNull PsiExpression rightOperand) {
if (rightOperand instanceof PsiBinaryExpression shiftingExpression) {
if (shiftingExpression.getOperationSign().getTokenType() != JavaTokenType.GTGTGT) return false;
PsiExpression leftSubOperand = shiftingExpression.getLOperand();
return EquivalenceChecker.getCanonicalPsiEquivalence()
.expressionsAreEquivalent(leftOperand, leftSubOperand) &&
Objects.equals(32, ExpressionUtils.computeConstantExpression(shiftingExpression.getROperand()));
}
return false;
}
public static class ReplaceWithLongHashCodeFix extends PsiUpdateModCommandQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return CommonQuickFixBundle.message("fix.replace.with.x", "Long.hashCode()");
}
@Override
protected void applyFix(@NotNull Project project, @NotNull PsiElement startElement, @NotNull ModPsiUpdater updater) {
PsiTypeCastExpression element = (PsiTypeCastExpression)startElement;
PsiExpression operand = getHashCodeOperand(element);
if (operand != null) {
CommentTracker ct = new CommentTracker();
ct.replace(element, "Long.hashCode(" + ct.text(operand) + ")");
}
}
}
}

View File

@@ -1742,6 +1742,10 @@
groupKey="group.names.language.level.specific.issues.and.migration.aids8" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="INFORMATION"
implementationClass="com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection"
key="inspection.convert.2.streamapi.display.name" bundle="messages.JavaBundle"/>
<localInspection groupPathKey="group.path.names.java.language.level.specific.issues.and.migration.aids" language="JAVA" shortName="UseHashCodeMethodInspection"
groupKey="group.names.language.level.specific.issues.and.migration.aids8" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.UseHashCodeMethodInspection"
key="inspection.convert.2.streamapi.display.name" bundle="messages.JavaBundle"/>
<localInspection groupPath="Java" language="JAVA" shortName="FuseStreamOperations"
bundle="messages.JavaBundle" key="inspection.fuse.stream.operations.display.name"
groupKey="group.names.code.style.issues" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"

View File

@@ -0,0 +1,22 @@
<html>
<body>
<p>
Informs you when bitwise operations can be replaced with the <code>Long.hashCode</code> method.
It detects instances of the pattern <code>(int)(x ^ (x >>> 32))</code> where <code>x</code> is a variable of type <code>long</code>.
This improves readability of code.
</p>
<p>
Example:
</p>
<pre><code>
int result = (int)(var ^ (var >>> 32));
</code></pre>
After applying the quick-fix:
<pre><code>
int result = Long.hashCode(var);
</code></pre>
<!-- tooltip end -->
<p>This inspection only reports if the language level of the project or module is 8 or higher.
<p><small>New in 2024.1</small></p>
</body>
</html>

View File

@@ -0,0 +1,8 @@
// "Replace with 'Long.hashCode()'" "true-preview"
public class Test {
long var = 1234567890123456789L;
public void testMethod() {
int result = Long.hashCode(var);
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Long.hashCode()'" "true-preview"
public class Test {
long var = 1234567890123456789L;
public void testMethod() {
int result = Long.hashCode(var);
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Long.hashCode()'" "true-preview"
public class Test {
long var = 1234567890123456789L;
public void testMethod() {
int result = (int<caret>)(var ^ (var >>> /*shift amount*/ 32));
}
}

View File

@@ -0,0 +1,8 @@
// "Replace with 'Long.hashCode()'" "true-preview"
public class Test {
long var = 1234567890123456789L;
public void testMethod() {
int result = (int<caret>)((var >>> (16+16)) ^ var);
}
}

View File

@@ -0,0 +1,27 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.UseHashCodeMethodInspection;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class UseHashCodeMethodInspectionTest extends LightIntentionActionTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/useHashCode";
}
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new UseHashCodeMethodInspection[]{new UseHashCodeMethodInspection()};
}
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return LightJavaCodeInsightFixtureTestCase.JAVA_11;
}
}