mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
[java-inspections] UseHashCodeMethodInspection: support double type
Also: fix comment processing Improvement of IDEA-338114 GitOrigin-RevId: 87d057ccbea4262c40e2717c2ea3c004ac9865b0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
e356e99136
commit
6e1d684df0
@@ -662,5 +662,4 @@ error.unnamed.variable.without.initializer=Unnamed variable declaration must hav
|
||||
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}
|
||||
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
|
||||
inspection.name.can.be.replaced.with.long.hashcode=Standard 'hashCode()' method can be used
|
||||
|
||||
@@ -7,10 +7,11 @@ import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiPrecedenceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
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 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;
|
||||
@@ -18,6 +19,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
private static final CallMatcher DOUBLE_TO_LONG_BITS =
|
||||
CallMatcher.staticCall("java.lang.Double", "doubleToLongBits").parameterTypes("double");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
@@ -25,18 +29,46 @@ public class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspection
|
||||
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());
|
||||
HashCodeModel model = getHashCodeModel(expression);
|
||||
if (model != null) {
|
||||
PsiClass containingClass = ClassUtils.getContainingClass(expression);
|
||||
if (containingClass != null && containingClass.getQualifiedName() != null &&
|
||||
containingClass.getQualifiedName().startsWith("java.lang.")) {
|
||||
// Avoid suggesting inside JDK sources
|
||||
return;
|
||||
}
|
||||
holder.registerProblem(expression,
|
||||
JavaAnalysisBundle.message("inspection.can.be.replaced.with.message", model.type + ".hashCode()"),
|
||||
new ReplaceWithLongHashCodeFix(model.type));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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());
|
||||
record HashCodeModel(@NotNull PsiExpression completeExpression,
|
||||
@NotNull PsiExpression argument,
|
||||
@Nullable PsiLocalVariable intermediateVariable,
|
||||
@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;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable HashCodeModel getHashCodeModel(PsiTypeCastExpression cast) {
|
||||
if (cast == null) return null;
|
||||
if (!PsiTypes.intType().equals(cast.getType())) return null;
|
||||
PsiExpression operand = PsiUtil.skipParenthesizedExprDown(cast.getOperand());
|
||||
if (!(operand instanceof PsiBinaryExpression binaryExpression)) return null;
|
||||
|
||||
PsiJavaToken operationSign = binaryExpression.getOperationSign();
|
||||
@@ -48,8 +80,8 @@ public class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspection
|
||||
if (leftOperand == null || rightOperand == null) return null;
|
||||
if (!PsiTypes.longType().equals(PsiPrimitiveType.getOptionallyUnboxedType(leftOperand.getType()))) return null;
|
||||
|
||||
if (isXorShift(leftOperand, rightOperand)) return leftOperand;
|
||||
if (isXorShift(rightOperand, leftOperand)) return rightOperand;
|
||||
if (isXorShift(leftOperand, rightOperand)) return new HashCodeModel(cast, leftOperand, null, "Long").tryReplaceDouble();
|
||||
if (isXorShift(rightOperand, leftOperand)) return new HashCodeModel(cast, rightOperand, null, "Long").tryReplaceDouble();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -67,25 +99,39 @@ public class UseHashCodeMethodInspection extends AbstractBaseJavaLocalInspection
|
||||
}
|
||||
|
||||
public static class ReplaceWithLongHashCodeFix extends PsiUpdateModCommandQuickFix {
|
||||
private final String myType;
|
||||
|
||||
public ReplaceWithLongHashCodeFix(String type) {
|
||||
myType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", myType+".hashCode()");
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", "Long.hashCode()");
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", "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) {
|
||||
PsiType type = operand.getType();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String call = PsiTypes.longType().equals(type)
|
||||
? "Long.hashCode(" + ct.text(operand) + ")"
|
||||
: ct.text(operand, PsiPrecedenceUtil.METHOD_CALL_PRECEDENCE) + ".hashCode()";
|
||||
ct.replace(element, call);
|
||||
HashCodeModel model = getHashCodeModel(element);
|
||||
if (model == null) return;
|
||||
PsiExpression argument = model.argument();
|
||||
PsiType type = argument.getType();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String call = type 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());
|
||||
}
|
||||
ct.replaceAndRestoreComments(element, call);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<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.
|
||||
Informs you when bitwise operations can be replaced with the <code>Long.hashCode()</code> or <code>Double.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> or
|
||||
the result of the previous <code>Double.doubleToLongBits()</code> call. This replacement shortens the code improves its readability.
|
||||
</p>
|
||||
<p>
|
||||
Example:
|
||||
|
||||
@@ -3,6 +3,7 @@ public class Test {
|
||||
Long var = 1234567890123456789L;
|
||||
|
||||
public void testMethod() {
|
||||
int result = var.hashCode();
|
||||
/*shift amount*/
|
||||
int result = var.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ public class Test {
|
||||
Long var1 = 1234567890123456784L;
|
||||
|
||||
public void testMethod(boolean f) {
|
||||
int result = (f ? var : var1).hashCode();
|
||||
/*shift amount*/
|
||||
int result = (f ? var : var1).hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
System.out.println(Double.hashCode(d));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(Double d) {
|
||||
/*comment*/
|
||||
System.out.println(d.hashCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Long.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
System.out.println(Long.hashCode(l));
|
||||
System.out.println(l);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Long.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
d++;
|
||||
System.out.println(Long.hashCode(l));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ public class Test {
|
||||
long var = 1234567890123456789L;
|
||||
|
||||
public void testMethod() {
|
||||
int result = Long.hashCode(var);
|
||||
/*shift amount*/
|
||||
int result = Long.hashCode(var);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
System.out.println((int) (l<caret> ^ (l >>> 32)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace with 'Double.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(Double d) {
|
||||
long l = Double.doubleToLongBits(/*comment*/d);
|
||||
System.out.println((int) (l ^ <caret>(l >>> 32)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Long.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
System.out.println((int) (l ^ (l<caret> >>> 32)));
|
||||
System.out.println(l);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Long.hashCode()'" "true-preview"
|
||||
public class Test {
|
||||
void test(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
d++;
|
||||
System.out.println((int) (l<caret> ^ (l >>> 32)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user