mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-386017 An inspection to suggest comparator.max/min methods since Java 26
(cherry picked from commit 242aca7f7293178ba915e5b004776e10ee561afa) IJ-CR-192581 GitOrigin-RevId: 3d886eac176097d2b93670a77bb387cd85cd09a2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
53e532e52b
commit
725067012d
@@ -2391,6 +2391,7 @@ markdown.documentation.comments.migration.display.name=Javadoc comment can be Ma
|
||||
markdown.documentation.comments.migration.fix=Convert to Markdown documentation comment
|
||||
|
||||
inspection.clamp.migration.display.name=Clamp function can be used
|
||||
comparator.min.max.can.be.used.display.name='Comparator.min()' or 'Comparator.max()' can be used
|
||||
|
||||
array.hash.code.fix.family.name=Replace with 'Arrays.hashCode()' call
|
||||
objects.hash.fix.family.name=Wrap with 'Arrays.hashCode()'
|
||||
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.migration;
|
||||
|
||||
import com.intellij.codeInspection.CommonQuickFixBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.PsiAssignmentExpression;
|
||||
import com.intellij.psi.PsiBinaryExpression;
|
||||
import com.intellij.psi.PsiConditionalExpression;
|
||||
import com.intellij.psi.PsiDeclarationStatement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiExpressionStatement;
|
||||
import com.intellij.psi.PsiIfStatement;
|
||||
import com.intellij.psi.PsiLocalVariable;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.PsiReturnStatement;
|
||||
import com.intellij.psi.PsiStatement;
|
||||
import com.intellij.psi.PsiYieldStatement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.EquivalenceChecker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import com.siyeh.ig.style.ConditionalModel;
|
||||
import com.siyeh.ig.style.IfConditionalModel;
|
||||
import com.siyeh.ig.style.SimplifiableIfStatementInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Inspection that detects ternary expressions and if statements using {@code Comparator.compare()} that can be
|
||||
* replaced with {@code Comparator.max()} or {@code Comparator.min()} methods available since Java 26.
|
||||
* <p>
|
||||
* Detected ternary patterns (where {@code comp} is a {@code Comparator} instance):
|
||||
* <ul>
|
||||
* <li>{@code comp.compare(a, b) > 0 ? a : b} → {@code comp.max(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) > 0 ? b : a} → {@code comp.min(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) < 0 ? a : b} → {@code comp.min(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) < 0 ? b : a} → {@code comp.max(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) >= 0 ? a : b} → {@code comp.max(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) >= 0 ? b : a} → {@code comp.min(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) <= 0 ? a : b} → {@code comp.min(a, b)}</li>
|
||||
* <li>{@code comp.compare(a, b) <= 0 ? b : a} → {@code comp.max(a, b)}</li>
|
||||
* </ul>
|
||||
* Detected if-statement patterns:
|
||||
* <ul>
|
||||
* <li>{@code if (comp.compare(a, b) > 0) return a; else return b;} → {@code return comp.max(a, b);}</li>
|
||||
* <li>{@code if (comp.compare(a, b) > 0) return a; return b;} → {@code return comp.max(a, b);}</li>
|
||||
* <li>{@code if (comp.compare(a, b) > 0) x = a; else x = b;} → {@code x = comp.max(a, b);}</li>
|
||||
* <li>{@code T x = b; if (comp.compare(a, b) > 0) x = a;} → {@code T x = comp.max(a, b);}</li>
|
||||
* </ul>
|
||||
* All comparison operators ({@code >}, {@code <}, {@code >=}, {@code <=}) are supported, as well as
|
||||
* reversed comparisons (e.g. {@code 0 < comp.compare(a, b)}).
|
||||
*/
|
||||
public final class ComparatorMinMaxCanBeUsedInspection extends BaseInspection {
|
||||
|
||||
private static final CallMatcher COMPARATOR_COMPARE =
|
||||
CallMatcher.instanceCall("java.util.Comparator", "compare").parameterCount(2);
|
||||
|
||||
@Override
|
||||
public @NotNull Set<JavaFeature> requiredFeatures() {
|
||||
return Set.of(JavaFeature.COMPARATOR_MIN_MAX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull String buildErrorString(Object... infos) {
|
||||
String methodName = (String)infos[0];
|
||||
return CommonQuickFixBundle.message("fix.can.replace.with.x", "Comparator." + methodName + "()");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix buildFix(Object... infos) {
|
||||
String methodName = (String)infos[0];
|
||||
return new ReplaceWithComparatorMinMaxFix(methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BaseInspectionVisitor buildVisitor() {
|
||||
return new ComparatorMinMaxVisitor();
|
||||
}
|
||||
|
||||
private record MinMaxInfo(
|
||||
@NotNull PsiExpression comparatorQualifier,
|
||||
@NotNull PsiExpression firstArg,
|
||||
@NotNull PsiExpression secondArg,
|
||||
@NotNull String methodName
|
||||
) {}
|
||||
|
||||
private static @Nullable MinMaxInfo extractMinMaxInfo(@NotNull ConditionalModel conditional) {
|
||||
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(conditional.getCondition());
|
||||
if (!(condition instanceof PsiBinaryExpression binExpr)) return null;
|
||||
|
||||
PsiExpression lhs = PsiUtil.skipParenthesizedExprDown(binExpr.getLOperand());
|
||||
PsiExpression rhs = PsiUtil.skipParenthesizedExprDown(binExpr.getROperand());
|
||||
IElementType tokenType = binExpr.getOperationTokenType();
|
||||
|
||||
PsiMethodCallExpression compareCall;
|
||||
RelationType relationType = DfaPsiUtil.getRelationByToken(tokenType);
|
||||
if (relationType == null) return null;
|
||||
if (lhs instanceof PsiMethodCallExpression lhsCall && COMPARATOR_COMPARE.test(lhsCall) && ExpressionUtils.isZero(rhs)) {
|
||||
compareCall = lhsCall;
|
||||
}
|
||||
else if (rhs instanceof PsiMethodCallExpression rhsCall && COMPARATOR_COMPARE.test(rhsCall) && ExpressionUtils.isZero(lhs)) {
|
||||
compareCall = rhsCall;
|
||||
relationType = relationType.getFlipped();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (relationType == null || relationType == RelationType.NE || !relationType.isInequality()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiExpression qualifier = compareCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
|
||||
PsiExpression[] args = compareCall.getArgumentList().getExpressions();
|
||||
if (args.length != 2) return null;
|
||||
|
||||
if (SideEffectChecker.mayHaveSideEffects(args[0]) || SideEffectChecker.mayHaveSideEffects(args[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiExpression thenExpr = PsiUtil.skipParenthesizedExprDown(conditional.getThenExpression());
|
||||
PsiExpression elseExpr = PsiUtil.skipParenthesizedExprDown(conditional.getElseExpression());
|
||||
if (thenExpr == null || elseExpr == null) return null;
|
||||
|
||||
PsiExpression compareFirst = PsiUtil.skipParenthesizedExprDown(args[0]);
|
||||
PsiExpression compareSecond = PsiUtil.skipParenthesizedExprDown(args[1]);
|
||||
if (compareFirst == null || compareSecond == null) return null;
|
||||
|
||||
EquivalenceChecker checker = EquivalenceChecker.getCanonicalPsiEquivalence();
|
||||
boolean thenIsFirst = checker.expressionsAreEquivalent(thenExpr, compareFirst) &&
|
||||
checker.expressionsAreEquivalent(elseExpr, compareSecond);
|
||||
boolean thenIsSecond = !thenIsFirst &&
|
||||
checker.expressionsAreEquivalent(thenExpr, compareSecond) &&
|
||||
checker.expressionsAreEquivalent(elseExpr, compareFirst);
|
||||
|
||||
if (!thenIsFirst && !thenIsSecond) return null;
|
||||
|
||||
String methodName = RelationType.GE.isSubRelation(relationType) == thenIsFirst ? "max" : "min";
|
||||
|
||||
return new MinMaxInfo(qualifier, args[0], args[1], methodName);
|
||||
}
|
||||
|
||||
private static class ComparatorMinMaxVisitor extends BaseInspectionVisitor {
|
||||
@Override
|
||||
public void visitConditionalExpression(@NotNull PsiConditionalExpression expression) {
|
||||
ConditionalModel model = ConditionalModel.from(expression);
|
||||
if (model == null) return;
|
||||
MinMaxInfo info = extractMinMaxInfo(model);
|
||||
if (info == null) return;
|
||||
registerError(expression, info.methodName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIfStatement(@NotNull PsiIfStatement statement) {
|
||||
ConditionalModel model = IfConditionalModel.from(statement, false);
|
||||
if (model != null) {
|
||||
MinMaxInfo info = extractMinMaxInfo(model);
|
||||
if (info == null) return;
|
||||
registerError(statement.getFirstChild(), info.methodName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReplaceWithComparatorMinMaxFix extends PsiUpdateModCommandQuickFix {
|
||||
private final String myMethodName;
|
||||
|
||||
ReplaceWithComparatorMinMaxFix(@NotNull String methodName) {
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", "Comparator." + myMethodName + "()");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiElement parent = PsiTreeUtil.getParentOfType(element, false, PsiConditionalExpression.class, PsiIfStatement.class);
|
||||
ConditionalModel model = switch (parent) {
|
||||
case PsiConditionalExpression conditional -> ConditionalModel.from(conditional);
|
||||
case PsiIfStatement statement -> IfConditionalModel.from(statement, false);
|
||||
case null, default -> null;
|
||||
};
|
||||
if (model == null) return;
|
||||
MinMaxInfo info = extractMinMaxInfo(model);
|
||||
if (info == null) return;
|
||||
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String callText = ct.text(info.comparatorQualifier()) +
|
||||
"." + info.methodName() + "(" +
|
||||
ct.text(info.firstArg()) + ", " +
|
||||
ct.text(info.secondArg()) + ")";
|
||||
|
||||
if (parent instanceof PsiIfStatement ifStatement && model instanceof IfConditionalModel ifModel) {
|
||||
replaceIfStatement(ct, ifStatement, ifModel, callText);
|
||||
}
|
||||
else {
|
||||
ct.replaceAndRestoreComments(parent, callText);
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceIfStatement(@NotNull CommentTracker ct,
|
||||
@NotNull PsiIfStatement ifStatement,
|
||||
@NotNull IfConditionalModel model,
|
||||
@NotNull String callText) {
|
||||
PsiStatement thenBranch = model.getThenBranch();
|
||||
PsiStatement elseBranch = model.getElseBranch();
|
||||
boolean elseIsOutside = !PsiTreeUtil.isAncestor(ifStatement, elseBranch, true);
|
||||
|
||||
if (elseIsOutside && elseBranch instanceof PsiDeclarationStatement statement) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)statement.getDeclaredElements()[0];
|
||||
PsiExpression initializer = var.getInitializer();
|
||||
if (initializer != null) {
|
||||
ct.replace(initializer, callText);
|
||||
}
|
||||
ct.deleteAndRestoreComments(ifStatement);
|
||||
}
|
||||
else {
|
||||
if (elseIsOutside) {
|
||||
ct.delete(elseBranch);
|
||||
}
|
||||
String statementText = buildStatementText(ct, thenBranch, model.getThenExpression(), callText);
|
||||
PsiElement result = ct.replaceAndRestoreComments(ifStatement, statementText);
|
||||
SimplifiableIfStatementInspection.tryJoinDeclaration(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull String buildStatementText(@NotNull CommentTracker ct,
|
||||
@NotNull PsiStatement thenBranch,
|
||||
@NotNull PsiExpression thenExpression,
|
||||
@NotNull String callText) {
|
||||
if (thenBranch instanceof PsiReturnStatement) {
|
||||
return "return " + callText + ";";
|
||||
}
|
||||
if (thenBranch instanceof PsiYieldStatement) {
|
||||
return "yield " + callText + ";";
|
||||
}
|
||||
if (thenBranch instanceof PsiExpressionStatement exprStmt &&
|
||||
exprStmt.getExpression() instanceof PsiAssignmentExpression assignment) {
|
||||
return ct.text(assignment.getLExpression()) + " = " + callText + ";";
|
||||
}
|
||||
// Fallback for method call pattern or other cases
|
||||
ct.replace(thenExpression, callText);
|
||||
return thenBranch.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1347,6 +1347,11 @@
|
||||
key="inspection.clamp.migration.display.name" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids21" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.siyeh.ig.migration.MathClampMigrationInspection"/>
|
||||
<localInspection groupPathKey="group.path.names.java.language.level.specific.issues.and.migration.aids" language="JAVA"
|
||||
shortName="ComparatorMinMaxCanBeUsed" bundle="messages.InspectionGadgetsBundle"
|
||||
key="comparator.min.max.can.be.used.display.name" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids26" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.siyeh.ig.migration.ComparatorMinMaxCanBeUsedInspection"/>
|
||||
|
||||
<!--group.names.java.language.level.issues-->
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="AnnotationClass" bundle="messages.InspectionGadgetsBundle" key="annotation.class.display.name"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports ternary expressions using <code>Comparator.compare()</code> that can be replaced
|
||||
with <code>Comparator.max()</code> or <code>Comparator.min()</code> methods available since Java 26.
|
||||
|
||||
<p>Example:</p>
|
||||
<pre><code>
|
||||
Comparator<String> comp = Comparator.naturalOrder();
|
||||
/*before*/ String result = comp.compare(a, b) > 0 ? a : b;
|
||||
/*after*/ String result = comp.max(a, b);
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
|
||||
<p><small>New in 2026.2</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -62,6 +62,7 @@ internal object DefaultJavaSyntaxResources {
|
||||
"feature.binary.literals" to "Binary literals",
|
||||
"feature.classfile.api" to "ClassFile API",
|
||||
"feature.collection.factories" to "Collection factory methods",
|
||||
"feature.comparator.min.max" to "Comparator min/max methods",
|
||||
"feature.diamond.types" to "Diamond types",
|
||||
"feature.effectively.final" to "Effectively final variables",
|
||||
"feature.enhanced.switch" to "Enhanced 'switch' blocks",
|
||||
|
||||
@@ -87,6 +87,7 @@ feature.markdown.comment=Markdown Documentation Comments
|
||||
feature.java.lang.io=java.lang.IO utility class
|
||||
feature.lazy.constants=Lazy constants
|
||||
feature.patterns.with.tightened.dominance=Patterns with tightened dominance
|
||||
feature.comparator.min.max=Comparator min/max methods
|
||||
|
||||
else.without.if='else' without 'if'
|
||||
expected.array.initializer=Array initializer expected
|
||||
|
||||
@@ -305,6 +305,8 @@ enum class JavaFeature {
|
||||
PATTERNS_WITH_TIGHTENED_DOMINANCE(LanguageLevel.JDK_26_PREVIEW, "feature.patterns.with.tightened.dominance"),
|
||||
|
||||
|
||||
COMPARATOR_MIN_MAX(LanguageLevel.JDK_26, "feature.comparator.min.max"),
|
||||
|
||||
VALHALLA_VALUE_CLASSES(LanguageLevel.JDK_X, "feature.valhalla.value.classes"),
|
||||
;
|
||||
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.siyeh.igtest.migration.comparator_min_max_can_be_used;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
class ComparatorMinMaxCanBeUsed {
|
||||
void testGreaterThan(Comparator<String> comp, String a, String b) {
|
||||
String r1 = comp.max(a, b);
|
||||
String r2 = comp.min(a, b);
|
||||
}
|
||||
|
||||
void testLessThan(Comparator<String> comp, String a, String b) {
|
||||
String r1 = comp.min(a, b);
|
||||
String r2 = comp.max(a, b);
|
||||
}
|
||||
|
||||
void testGreaterThanOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r1 = comp.max(a, b);
|
||||
String r2 = comp.min(a, b);
|
||||
}
|
||||
|
||||
void testLessThanOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r1 = comp.min(a, b);
|
||||
String r2 = comp.max(a, b);
|
||||
}
|
||||
|
||||
void testReversedComparison(Comparator<String> comp, String a, String b) {
|
||||
// 0 < compare(a, b) is equivalent to compare(a, b) > 0
|
||||
String r1 = comp.max(a, b);
|
||||
// 0 >= compare(a, b) is equivalent to compare(a, b) <= 0
|
||||
String r2 = comp.max(a, b);
|
||||
}
|
||||
|
||||
void testParenthesized(Comparator<String> comp, String a, String b) {
|
||||
String r1 = comp.max(a, b);
|
||||
String r2 = comp.min(a, b);
|
||||
}
|
||||
|
||||
// If-statement patterns
|
||||
String testIfReturnGreaterThan(Comparator<String> comp, String a, String b) {
|
||||
return comp.max(a, b);
|
||||
}
|
||||
|
||||
String testIfReturnLessThan(Comparator<String> comp, String a, String b) {
|
||||
return comp.min(a, b);
|
||||
}
|
||||
|
||||
String testIfReturnReversedBranches(Comparator<String> comp, String a, String b) {
|
||||
return comp.min(a, b);
|
||||
}
|
||||
|
||||
String testIfReturnWithBlocks(Comparator<String> comp, String a, String b) {
|
||||
return comp.max(a, b);
|
||||
}
|
||||
|
||||
String testIfImplicitReturn(Comparator<String> comp, String a, String b) {
|
||||
return comp.max(a, b);
|
||||
}
|
||||
|
||||
void testIfAssignment(Comparator<String> comp, String a, String b) {
|
||||
String r = comp.max(a, b);
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfOverwrittenDeclaration(Comparator<String> comp, String a, String b) {
|
||||
String r = comp.max(a, b);
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfGreaterOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r = comp.max(a, b);
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfReversedComparison(Comparator<String> comp, String a, String b) {
|
||||
String r = comp.max(a, b);
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfNoWarning(Comparator<String> comp, String a, String b) {
|
||||
// Non-matching branches
|
||||
String r;
|
||||
if (comp.compare(a, b) > 0) r = a; else r = "default";
|
||||
// Comparison to non-zero
|
||||
if (comp.compare(a, b) > 1) r = a; else r = b;
|
||||
// Equality comparison
|
||||
if (comp.compare(a, b) == 0) r = a; else r = b;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testNoWarning(Comparator<String> comp, String a, String b) {
|
||||
// Side effects in compare arguments
|
||||
String r1 = comp.max(a.trim(), b.trim());
|
||||
// Non-matching branches
|
||||
String r2 = comp.compare(a, b) > 0 ? a : "default";
|
||||
// Comparison to non-zero
|
||||
String r3 = comp.compare(a, b) > 1 ? a : b;
|
||||
// Equality comparison
|
||||
String r4 = comp.compare(a, b) == 0 ? a : b;
|
||||
// Not a Comparator.compare call
|
||||
int cmp = comp.compare(a, b);
|
||||
String r5 = cmp > 0 ? a : b;
|
||||
// Branches swapped with unrelated expressions
|
||||
String r6 = comp.compare(a, b) > 0 ? b : b;
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package com.siyeh.igtest.migration.comparator_min_max_can_be_used;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
class ComparatorMinMaxCanBeUsed {
|
||||
void testGreaterThan(Comparator<String> comp, String a, String b) {
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.max()'"><caret>comp.compare(a, b) > 0 ? a : b</warning>;
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.min()'">comp.compare(a, b) > 0 ? b : a</warning>;
|
||||
}
|
||||
|
||||
void testLessThan(Comparator<String> comp, String a, String b) {
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.min()'">comp.compare(a, b) < 0 ? a : b</warning>;
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.max()'">comp.compare(a, b) < 0 ? b : a</warning>;
|
||||
}
|
||||
|
||||
void testGreaterThanOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.max()'">comp.compare(a, b) >= 0 ? a : b</warning>;
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.min()'">comp.compare(a, b) >= 0 ? b : a</warning>;
|
||||
}
|
||||
|
||||
void testLessThanOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.min()'">comp.compare(a, b) <= 0 ? a : b</warning>;
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.max()'">comp.compare(a, b) <= 0 ? b : a</warning>;
|
||||
}
|
||||
|
||||
void testReversedComparison(Comparator<String> comp, String a, String b) {
|
||||
// 0 < compare(a, b) is equivalent to compare(a, b) > 0
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.max()'">0 < comp.compare(a, b) ? a : b</warning>;
|
||||
// 0 >= compare(a, b) is equivalent to compare(a, b) <= 0
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.max()'">0 >= comp.compare(a, b) ? b : a</warning>;
|
||||
}
|
||||
|
||||
void testParenthesized(Comparator<String> comp, String a, String b) {
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.max()'">(comp.compare(a, b)) > 0 ? a : b</warning>;
|
||||
String r2 = <warning descr="Can be replaced with 'Comparator.min()'">comp.compare(a, b) > 0 ? (b) : (a)</warning>;
|
||||
}
|
||||
|
||||
// If-statement patterns
|
||||
String testIfReturnGreaterThan(Comparator<String> comp, String a, String b) {
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) > 0) return a; else return b;
|
||||
}
|
||||
|
||||
String testIfReturnLessThan(Comparator<String> comp, String a, String b) {
|
||||
<warning descr="Can be replaced with 'Comparator.min()'">if</warning> (comp.compare(a, b) < 0) return a; else return b;
|
||||
}
|
||||
|
||||
String testIfReturnReversedBranches(Comparator<String> comp, String a, String b) {
|
||||
<warning descr="Can be replaced with 'Comparator.min()'">if</warning> (comp.compare(a, b) > 0) return b; else return a;
|
||||
}
|
||||
|
||||
String testIfReturnWithBlocks(Comparator<String> comp, String a, String b) {
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) > 0) {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
String testIfImplicitReturn(Comparator<String> comp, String a, String b) {
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) > 0) return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
void testIfAssignment(Comparator<String> comp, String a, String b) {
|
||||
String r;
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) > 0) r = a; else r = b;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfOverwrittenDeclaration(Comparator<String> comp, String a, String b) {
|
||||
String r = b;
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) > 0) r = a;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfGreaterOrEqual(Comparator<String> comp, String a, String b) {
|
||||
String r;
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (comp.compare(a, b) >= 0) r = a; else r = b;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfReversedComparison(Comparator<String> comp, String a, String b) {
|
||||
String r;
|
||||
<warning descr="Can be replaced with 'Comparator.max()'">if</warning> (0 < comp.compare(a, b)) r = a; else r = b;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testIfNoWarning(Comparator<String> comp, String a, String b) {
|
||||
// Non-matching branches
|
||||
String r;
|
||||
if (comp.compare(a, b) > 0) r = a; else r = "default";
|
||||
// Comparison to non-zero
|
||||
if (comp.compare(a, b) > 1) r = a; else r = b;
|
||||
// Equality comparison
|
||||
if (comp.compare(a, b) == 0) r = a; else r = b;
|
||||
System.out.println(r);
|
||||
}
|
||||
|
||||
void testNoWarning(Comparator<String> comp, String a, String b) {
|
||||
// Side effects in compare arguments
|
||||
String r1 = <warning descr="Can be replaced with 'Comparator.max()'">comp.compare(a.trim(), b.trim()) > 0 ? a.trim() : b.trim()</warning>;
|
||||
// Non-matching branches
|
||||
String r2 = comp.compare(a, b) > 0 ? a : "default";
|
||||
// Comparison to non-zero
|
||||
String r3 = comp.compare(a, b) > 1 ? a : b;
|
||||
// Equality comparison
|
||||
String r4 = comp.compare(a, b) == 0 ? a : b;
|
||||
// Not a Comparator.compare call
|
||||
int cmp = comp.compare(a, b);
|
||||
String r5 = cmp > 0 ? a : b;
|
||||
// Branches swapped with unrelated expressions
|
||||
String r6 = comp.compare(a, b) > 0 ? b : b;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.migration;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
|
||||
public final class ComparatorMinMaxCanBeUsedInspectionTest extends LightJavaInspectionTestCase {
|
||||
|
||||
public void testComparatorMinMaxCanBeUsed() {
|
||||
doTest();
|
||||
checkQuickFixAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new ComparatorMinMaxCanBeUsedInspection();
|
||||
}
|
||||
}
|
||||
@@ -212,6 +212,7 @@ group.names.language.level.specific.issues.and.migration.aids16=Java 16
|
||||
group.names.language.level.specific.issues.and.migration.aids21=Java 21
|
||||
group.names.language.level.specific.issues.and.migration.aids23=Java 23
|
||||
group.names.language.level.specific.issues.and.migration.aids25=Java 25
|
||||
group.names.language.level.specific.issues.and.migration.aids26=Java 26
|
||||
group.names.javabeans.issues=JavaBeans issues
|
||||
group.names.inheritance.issues=Inheritance issues
|
||||
group.names.data.flow.issues=Data flow
|
||||
|
||||
Reference in New Issue
Block a user