mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJ-CR-145218 [java-inspections] IDEA-201932 Provide inspection to highlight redundant creation operations in java date time api
- combine into one inspection - fix messages GitOrigin-RevId: d4e064948f8c730c4d68c58e6c9b1277c6b66b4d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a7dfa7615e
commit
f474d8047b
@@ -1673,15 +1673,17 @@ if.may.be.factorized.problem.descriptor=<code>#ref</code> can be factorized #loc
|
||||
if.may.be.factorized.quickfix=Replace with factorized expression
|
||||
redundant.string.format.call.display.name=Redundant call to 'String.format()'
|
||||
redundant.call.problem.descriptor=Redundant call to <code>#ref()</code> #loc
|
||||
inspection.explicit.chrono.field.display.name=Calls of 'java.time' methods with explicit 'ChronoField' or 'ChronoUnit' arguments can be simplified
|
||||
inspection.explicit.chrono.field.problem.descriptor=Calls with explicit 'ChronoField' or 'ChronoUnit' arguments call can be simplified
|
||||
inspection.explicit.chrono.field.family.name=Simplify calls with explicit 'ChronoField' or 'ChronoUnit' arguments
|
||||
inspection.redundant.creation.java.time.display.name=Redundant creation of 'java.time' objects
|
||||
inspection.redundant.creation.java.time.error.message=Redundant creation of ''{0}'' object
|
||||
inspection.redundant.creation.java.time.family.name=Simplify creation of 'java.time' object
|
||||
inspection.simplifiable.compare.java.time.display.name=Expression with 'java.time' 'compareTo()' call can be simplified
|
||||
inspection.simplifiable.compare.java.time.family.name=Simplify expression with 'java.time' 'compareTo()' call
|
||||
inspection.simplifiable.compare.java.time.problem.descriptor=Expression with 'java.time' <code>#ref()</code> call can be simplified
|
||||
inspection.redundant.java.time.operation.display.name=Redundant operation on 'java.time' object
|
||||
inspection.redundant.java.time.operation.explicit.chrono.field.family.name=Simplify calls with explicit 'ChronoField' or 'ChronoUnit' arguments
|
||||
inspection.redundant.java.time.operation.explicit.chrono.field.problem.descriptor=Calls with explicit 'ChronoField' or 'ChronoUnit' arguments call can be simplified
|
||||
inspection.redundant.java.time.operation.creation.java.time.redundant.call.message=Redundant ''{0}'' call
|
||||
inspection.redundant.java.time.operation.creation.java.time.error.message=Redundant creation of ''{0}'' object
|
||||
inspection.redundant.java.time.operation.creation.java.time.error.remove.fix.message=Remove ''{0}'' call
|
||||
inspection.redundant.java.time.operation.creation.java.time.error.replace.fix.message=Replace with ''{0}'' call
|
||||
inspection.redundant.java.time.operation.creation.java.time.family.name=Simplify creation of 'java.time' object
|
||||
inspection.redundant.java.time.operation.creation.java.time.remove.family.name=Remove unnecessary call
|
||||
inspection.redundant.java.time.operation.compare.java.time.family.name=Simplify expression with 'java.time' 'compareTo()' call
|
||||
inspection.redundant.java.time.operation.compare.java.time.problem.descriptor=Expression with 'java.time' <code>#ref()</code> call can be simplified
|
||||
redundant.string.format.call.quickfix=Remove redundant call to 'String.format()'
|
||||
redundant.string.formatted.call.quickfix=Remove redundant call to 'String.formatted()'
|
||||
equals.called.on.enum.constant.display.name='equals()' called on enum value
|
||||
|
||||
@@ -2676,21 +2676,12 @@
|
||||
groupKey="group.names.verbose.or.redundant.code.constructs" enabledByDefault="true" level="WARNING"
|
||||
editorAttributes="NOT_USED_ELEMENT_ATTRIBUTES"
|
||||
implementationClass="com.siyeh.ig.controlflow.UnnecessaryReturnInspection" cleanupTool="true"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantCompareToJavaTime"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" key="inspection.simplifiable.compare.java.time.display.name"
|
||||
bundle="messages.InspectionGadgetsBundle" implementationClass="com.intellij.codeInspection.RedundantCompareToJavaTimeInspection"
|
||||
cleanupTool="true"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="RedundantExplicitChronoField"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WARNING" key="inspection.explicit.chrono.field.display.name"
|
||||
bundle="messages.InspectionGadgetsBundle" implementationClass="com.siyeh.ig.redundancy.RedundantExplicitChronoFieldInspection"
|
||||
cleanupTool="true"/>
|
||||
<localInspection groupPath="Java" language="JAVA"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.verbose.or.redundant.code.constructs"
|
||||
enabledByDefault="true" level="WEAK WARNING" key="inspection.redundant.creation.java.time.display.name"
|
||||
bundle="messages.InspectionGadgetsBundle" implementationClass="com.siyeh.ig.redundancy.RedundantCreationJavaTimeInspection"
|
||||
enabledByDefault="true" level="WARNING" key="inspection.redundant.java.time.operation.display.name"
|
||||
bundle="messages.InspectionGadgetsBundle" implementationClass="com.siyeh.ig.redundancy.RedundantJavaTimeOperationsInspection"
|
||||
cleanupTool="true"/>
|
||||
<inspectionElementsMerger implementation="com.siyeh.ig.redundancy.RedundantJavaTimeOperationMerger"/>
|
||||
|
||||
<!--group.names.visibility.issues-->
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="AmbiguousMethodCall" bundle="messages.InspectionGadgetsBundle" key="ambiguous.method.call.display.name"
|
||||
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
// 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.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.openapi.util.NlsSafe;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class RedundantCompareToJavaTimeInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
|
||||
|
||||
private static final String JAVA_TIME_LOCAL_DATE = "java.time.LocalDate";
|
||||
private static final String JAVA_TIME_LOCAL_DATE_TIME = "java.time.LocalDateTime";
|
||||
private static final String JAVA_TIME_LOCAL_TIME = "java.time.LocalTime";
|
||||
private static final String JAVA_TIME_OFFSET_DATE_TIME = "java.time.OffsetDateTime";
|
||||
private static final String JAVA_TIME_OFFSET_TIME = "java.time.OffsetTime";
|
||||
|
||||
private static final CallMatcher COMPARE_TO_METHODS = CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "compareTo").parameterTypes("java.time.chrono.ChronoLocalDate"),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "compareTo").parameterTypes(JAVA_TIME_LOCAL_TIME),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "compareTo")
|
||||
.parameterTypes("java.time.chrono.ChronoLocalDateTime"),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_TIME, "compareTo").parameterTypes(JAVA_TIME_OFFSET_TIME),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "compareTo")
|
||||
.parameterTypes(JAVA_TIME_OFFSET_DATE_TIME)
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
|
||||
if (!COMPARE_TO_METHODS.test(call)) return;
|
||||
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (nameElement == null) return;
|
||||
|
||||
final PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression == null) {
|
||||
return;
|
||||
}
|
||||
PsiType[] types = call.getArgumentList().getExpressionTypes();
|
||||
if (types.length != 1) {
|
||||
return;
|
||||
}
|
||||
final PsiType argumentType = types[0];
|
||||
if (argumentType == null || !argumentType.equals(qualifierExpression.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiBinaryExpression binOp = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiBinaryExpression.class);
|
||||
if (binOp == null) return;
|
||||
RelationType relationType = DfaPsiUtil.getRelationByToken(binOp.getOperationTokenType());
|
||||
if (relationType == RelationType.IS || relationType == RelationType.IS_NOT) {
|
||||
return;
|
||||
}
|
||||
if (relationType == null) return;
|
||||
if (ExpressionUtils.isZero(binOp.getLOperand())) {
|
||||
relationType = relationType.getFlipped();
|
||||
if (relationType == null) return;
|
||||
}
|
||||
else if (!ExpressionUtils.isZero(binOp.getROperand())) {
|
||||
return;
|
||||
}
|
||||
holder.registerProblem(nameElement,
|
||||
InspectionGadgetsBundle.message("inspection.simplifiable.compare.java.time.problem.descriptor"),
|
||||
new InlineCompareToTimeCallFix(relationType, argumentType.getCanonicalText()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class InlineCompareToTimeCallFix extends PsiUpdateModCommandQuickFix {
|
||||
private final @NotNull RelationType myRelationType;
|
||||
private final @NotNull String myArgumentType;
|
||||
|
||||
InlineCompareToTimeCallFix(@NotNull RelationType relationType, @NotNull @NlsSafe String argumentType) {
|
||||
myRelationType = relationType;
|
||||
myArgumentType = argumentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
String method = getMethodName();
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x.call", method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.simplifiable.compare.java.time.family.name");
|
||||
}
|
||||
|
||||
private @NotNull String getMethodName() {
|
||||
return switch (myRelationType) {
|
||||
case EQ, NE -> myArgumentType.equals(JAVA_TIME_LOCAL_TIME) ? "equals" : "isEqual";
|
||||
case GT, LE -> "isAfter";
|
||||
case LT, GE -> "isBefore";
|
||||
default -> throw new UnsupportedOperationException(myRelationType.toString());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression first = call.getMethodExpression().getQualifierExpression();
|
||||
if (first == null) {
|
||||
return;
|
||||
}
|
||||
PsiExpression second = call.getArgumentList().getExpressions()[0];
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String text = ct.text(first) + "." + getMethodName() + "(" + ct.text(second) + ")";
|
||||
|
||||
if (myRelationType == RelationType.NE || myRelationType == RelationType.LE || myRelationType == RelationType.GE) {
|
||||
text = "!" + text;
|
||||
}
|
||||
PsiBinaryExpression parent = PsiTreeUtil.getParentOfType(call, PsiBinaryExpression.class);
|
||||
if (parent == null) return;
|
||||
ct.replaceAndRestoreComments(parent, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.modcommand.ModCommandQuickFix;
|
||||
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.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.*;
|
||||
|
||||
|
||||
public final class RedundantCreationJavaTimeInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
|
||||
private static final CallMatcher FROM_MATCHER = CallMatcher.anyOf(
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_TIME, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE_TIME, "from")
|
||||
.parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_OFFSET_DATE_TIME, "from")
|
||||
.parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_OFFSET_TIME, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_ZONED_DATE_TIME, "from")
|
||||
.parameterCount(1));
|
||||
|
||||
private static final CallMatcher LOCAL_DATE_OF_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "of").parameterTypes("int", "int", "int"),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "of").parameterTypes("int", "java.time.Month", "int"));
|
||||
|
||||
private static final CallMatcher GET_YEAR_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getYear").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MONTH_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMonth").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MONTH_VALUE_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMonthValue").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_DAY_OF_MONTH_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getDayOfMonth").parameterCount(0));
|
||||
|
||||
private static final CallMatcher LOCAL_TIME_OF_MATCHER =
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_TIME, "of").parameterTypes("int", "int", "int", "int");
|
||||
|
||||
private static final CallMatcher GET_HOUR_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getHour").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MINUTE_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMinute").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_SECOND_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getSecond").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_NANO_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getNano").parameterCount(0));
|
||||
private static final String TO_LOCAL_TIME = "toLocalTime";
|
||||
private static final String TO_LOCAL_DATE = "toLocalDate";
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@SuppressWarnings("UnnecessaryReturnStatement")
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
|
||||
if (fixRedundantFrom(call)) return;
|
||||
if (fixRedundantOfLocalDate(call)) return;
|
||||
if (fixRedundantOfLocalTime(call)) return;
|
||||
}
|
||||
|
||||
|
||||
private boolean fixRedundantOfLocalTime(@NotNull PsiMethodCallExpression call) {
|
||||
if (!LOCAL_TIME_OF_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 4) return false;
|
||||
if (!(arguments[0] instanceof PsiMethodCallExpression firstArgumentCall)) return false;
|
||||
if (!GET_HOUR_MATCHER.test(firstArgumentCall)) return false;
|
||||
if (!(arguments[1] instanceof PsiMethodCallExpression secondArgumentCall)) return false;
|
||||
if (!GET_MINUTE_MATCHER.test(secondArgumentCall)) return false;
|
||||
if (!(arguments[2] instanceof PsiMethodCallExpression thirdArgumentCall)) return false;
|
||||
if (!GET_SECOND_MATCHER.test(thirdArgumentCall)) return false;
|
||||
if (!(arguments[3] instanceof PsiMethodCallExpression fourthArgumentCall)) return false;
|
||||
if (!GET_NANO_MATCHER.test(fourthArgumentCall)) return false;
|
||||
PsiExpression expression1 = firstArgumentCall.getMethodExpression().getQualifierExpression();
|
||||
if (!(PsiUtil.skipParenthesizedExprDown(expression1) instanceof PsiReferenceExpression referenceExpression1 &&
|
||||
referenceExpression1.resolve() instanceof PsiVariable variable1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(areSameVariableReferences(holder.getProject(),
|
||||
expression1,
|
||||
secondArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
thirdArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
fourthArgumentCall.getMethodExpression().getQualifierExpression()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass variableClass = PsiUtil.resolveClassInClassTypeOnly(variable1.getType());
|
||||
if (variableClass == null) return false;
|
||||
PsiElement referenceNameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (referenceNameElement == null) return false;
|
||||
|
||||
PsiIdentifier identifier = variable1.getNameIdentifier();
|
||||
if (identifier == null) return false;
|
||||
if(JAVA_TIME_LOCAL_TIME.equals(variableClass.getQualifiedName())) {
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.error.message", "LocalTime"),
|
||||
RedundantCreationFix.create(identifier.getText()));
|
||||
return true;
|
||||
}
|
||||
PsiMethod[] localTimes = variableClass.findMethodsByName(TO_LOCAL_TIME, false);
|
||||
if (localTimes.length != 1) return false;
|
||||
|
||||
String newText = identifier.getText() + "." + TO_LOCAL_TIME + "()";
|
||||
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.error.message", "LocalTime"),
|
||||
RedundantCreationFix.create(newText));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean fixRedundantOfLocalDate(@NotNull PsiMethodCallExpression call) {
|
||||
if (!LOCAL_DATE_OF_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 3) return false;
|
||||
if (!(arguments[0] instanceof PsiMethodCallExpression firstArgumentCall)) return false;
|
||||
if (!GET_YEAR_MATCHER.test(firstArgumentCall)) return false;
|
||||
if (!(arguments[1] instanceof PsiMethodCallExpression secondArgumentCall)) return false;
|
||||
if (!GET_MONTH_VALUE_MATCHER.test(secondArgumentCall) && !GET_MONTH_MATCHER.test(secondArgumentCall)) return false;
|
||||
if (!(arguments[2] instanceof PsiMethodCallExpression thirdArgumentCall)) return false;
|
||||
if (!GET_DAY_OF_MONTH_MATCHER.test(thirdArgumentCall)) return false;
|
||||
|
||||
PsiExpression firstArgument = PsiUtil.skipParenthesizedExprDown(firstArgumentCall.getMethodExpression().getQualifierExpression());
|
||||
if (!(firstArgument instanceof PsiReferenceExpression referenceExpression &&
|
||||
referenceExpression.resolve() instanceof PsiVariable firstVariable)) return false;
|
||||
if (!areSameVariableReferences(holder.getProject(),
|
||||
firstArgument,
|
||||
secondArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
thirdArgumentCall.getMethodExpression().getQualifierExpression())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass variableClass = PsiUtil.resolveClassInClassTypeOnly(firstArgument.getType());
|
||||
if (variableClass == null) return false;
|
||||
PsiElement referenceNameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (referenceNameElement == null) return false;
|
||||
PsiIdentifier identifier = firstVariable.getNameIdentifier();
|
||||
if (identifier == null) return false;
|
||||
|
||||
if (JAVA_TIME_LOCAL_DATE.equals(variableClass.getQualifiedName())) {
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.error.message", "LocalDate"),
|
||||
RedundantCreationFix.create(identifier.getText()));
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiMethod[] localDates = variableClass.findMethodsByName(TO_LOCAL_DATE, false);
|
||||
if (localDates.length != 1) return false;
|
||||
String newText = identifier.getText() + "." + TO_LOCAL_DATE + "()";
|
||||
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.error.message", "LocalDate"),
|
||||
RedundantCreationFix.create(newText));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean areSameVariableReferences(@NotNull Project project, PsiExpression... expressions) {
|
||||
if (expressions.length == 0) return false;
|
||||
PsiManager psiManager = PsiManager.getInstance(project);
|
||||
PsiVariable firstVariable = resolveVariable(expressions[0]);
|
||||
if (firstVariable == null) return false;
|
||||
for (PsiExpression expression : expressions) {
|
||||
PsiVariable variable = resolveVariable(expression);
|
||||
if (variable == null || !psiManager.areElementsEquivalent(firstVariable, variable)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiVariable resolveVariable(@Nullable PsiExpression expression) {
|
||||
if (expression == null) return null;
|
||||
PsiExpression unwrappedExpression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (!(unwrappedExpression instanceof PsiReferenceExpression referenceExpression)) return null;
|
||||
PsiElement resolvedElement = referenceExpression.resolve();
|
||||
return (resolvedElement instanceof PsiVariable variable) ? variable : null;
|
||||
}
|
||||
|
||||
private boolean fixRedundantFrom(@NotNull PsiMethodCallExpression call) {
|
||||
if (!FROM_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 1) return false;
|
||||
PsiExpression expression = arguments[0];
|
||||
PsiClass classOfArgument = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) return false;
|
||||
PsiClass classOfMethod = method.getContainingClass();
|
||||
if (classOfMethod == null) return false;
|
||||
PsiManager manager = classOfMethod.getManager();
|
||||
if (!manager.areElementsEquivalent(classOfArgument, classOfMethod)) return false;
|
||||
String newText = expression.getText();
|
||||
if (newText == null) return false;
|
||||
PsiElement identifier = call.getMethodExpression().getReferenceNameElement();
|
||||
if (identifier == null) return false;
|
||||
String className = classOfMethod.getName();
|
||||
if (className == null) return false;
|
||||
holder.registerProblem(identifier,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.error.message", className),
|
||||
RedundantCreationFix.create(newText));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class RedundantCreationFix extends PsiUpdateModCommandQuickFix {
|
||||
@NotNull private final String myNewText;
|
||||
|
||||
private RedundantCreationFix(@NotNull String text) { myNewText = text; }
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class, false);
|
||||
if (callExpression == null) return;
|
||||
new CommentTracker().replaceAndRestoreComments(callExpression, myNewText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.redundant.creation.java.time.family.name");
|
||||
}
|
||||
|
||||
private static @NotNull ModCommandQuickFix create(@NotNull String newText) {
|
||||
return new RedundantCreationFix(newText);
|
||||
}
|
||||
}
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool;
|
||||
import com.intellij.codeInspection.CommonQuickFixBundle;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.util.ChronoUtil;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
||||
public final class RedundantExplicitChronoFieldInspection extends AbstractBaseJavaLocalInspectionTool
|
||||
implements CleanupLocalInspectionTool {
|
||||
private static final CallMatcher CAN_BE_SIMPLIFIED_MATCHERS = CallMatcher.anyOf(
|
||||
ChronoUtil.CHRONO_GET_MATCHERS,
|
||||
ChronoUtil.CHRONO_WITH_MATCHERS,
|
||||
ChronoUtil.CHRONO_PLUS_MINUS_MATCHERS
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
|
||||
String methodName = call.getMethodExpression().getReferenceName();
|
||||
if (!"get".equals(methodName) && !"with".equals(methodName) &&
|
||||
!"plus".equals(methodName) && !"minus".equals(methodName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CAN_BE_SIMPLIFIED_MATCHERS.matches(call)) return;
|
||||
int fieldArgumentIndex = 1;
|
||||
if ("get".equals(methodName) || "with".equals(methodName)) {
|
||||
fieldArgumentIndex = 0;
|
||||
}
|
||||
PsiExpression[] expressions = call.getArgumentList().getExpressions();
|
||||
if (expressions.length < fieldArgumentIndex + 1) {
|
||||
return;
|
||||
}
|
||||
@Nullable PsiExpression fieldExpression = expressions[fieldArgumentIndex];
|
||||
String chronoEnumName = getNameOfChronoEnum(fieldExpression, methodName);
|
||||
if (chronoEnumName == null) return;
|
||||
String newMethodName = getNewMethodName(chronoEnumName, call);
|
||||
if (newMethodName == null) return;
|
||||
PsiElement identifier = getIdentifier(call.getMethodExpression());
|
||||
if (identifier == null) return;
|
||||
holder.registerProblem(identifier,
|
||||
InspectionGadgetsBundle.message("inspection.explicit.chrono.field.problem.descriptor"),
|
||||
new InlineChronoEnumCallFix(newMethodName, fieldArgumentIndex));
|
||||
}
|
||||
|
||||
private static @Nullable String getNewMethodName(@NotNull String chronoEnumName, @NotNull PsiMethodCallExpression call) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isAvailableCall(method, chronoEnumName)) {
|
||||
return null;
|
||||
}
|
||||
//'with(ChronoField, long)' can be converted only to 'with...(int)'
|
||||
if ("with".equals(method.getName())) {
|
||||
PsiType[] types = call.getArgumentList().getExpressionTypes();
|
||||
if (types.length != 2 || types[1] == null || TypeConversionUtil.getTypeRank(types[1]) > TypeConversionUtil.INT_RANK) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String methodName = method.getName();
|
||||
return findEquivalentMethod(chronoEnumName, methodName);
|
||||
}
|
||||
|
||||
private static boolean isAvailableCall(@NotNull PsiMethod method, @NotNull String chronoEnumName) {
|
||||
return switch (method.getName()) {
|
||||
case "get" -> ChronoUtil.isAnyGetSupported(method, ChronoUtil.getChronoField(chronoEnumName));
|
||||
case "with" -> ChronoUtil.isWithSupported(method, ChronoUtil.getChronoField(chronoEnumName));
|
||||
case "plus", "minus" -> ChronoUtil.isPlusMinusSupported(method, ChronoUtil.getChronoUnit(chronoEnumName));
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable String findEquivalentMethod(@NotNull String chronoEnumName, @NotNull String methodName) {
|
||||
return switch (methodName) {
|
||||
case "plus" -> switch (chronoEnumName) {
|
||||
case "NANOS" -> "plusNanos";
|
||||
case "SECONDS" -> "plusSeconds";
|
||||
case "MINUTES" -> "plusMinutes";
|
||||
case "HOURS" -> "plusHours";
|
||||
case "DAYS" -> "plusDays";
|
||||
case "WEEKS" -> "plusWeeks";
|
||||
case "MONTHS" -> "plusMonths";
|
||||
case "YEARS" -> "plusYears";
|
||||
default -> null;
|
||||
};
|
||||
case "minus" -> switch (chronoEnumName) {
|
||||
case "NANOS" -> "minusNanos";
|
||||
case "SECONDS" -> "minusSeconds";
|
||||
case "MINUTES" -> "minusMinutes";
|
||||
case "HOURS" -> "minusHours";
|
||||
case "DAYS" -> "minusDays";
|
||||
case "WEEKS" -> "minusWeeks";
|
||||
case "MONTHS" -> "minusMonths";
|
||||
case "YEARS" -> "minusYears";
|
||||
default -> null;
|
||||
};
|
||||
case "get" -> switch (chronoEnumName) {
|
||||
case "NANO_OF_SECOND" -> "getNano";
|
||||
case "SECOND_OF_MINUTE" -> "getSecond";
|
||||
case "MINUTE_OF_HOUR" -> "getMinute";
|
||||
case "HOUR_OF_DAY" -> "getHour";
|
||||
case "DAY_OF_MONTH" -> "getDayOfMonth";
|
||||
case "DAY_OF_YEAR" -> "getDayOfYear";
|
||||
case "MONTH_OF_YEAR" -> "getMonth";
|
||||
case "YEAR" -> "getYear";
|
||||
default -> null;
|
||||
};
|
||||
case "with" -> switch (chronoEnumName) {
|
||||
case "NANO_OF_SECOND" -> "withNano";
|
||||
case "SECOND_OF_MINUTE" -> "withSecond";
|
||||
case "MINUTE_OF_HOUR" -> "withMinute";
|
||||
case "HOUR_OF_DAY" -> "withHour";
|
||||
case "DAY_OF_MONTH" -> "withDayOfMonth";
|
||||
case "DAY_OF_YEAR" -> "withDayOfYear";
|
||||
case "MONTH_OF_YEAR" -> "withMonth";
|
||||
case "YEAR" -> "withYear";
|
||||
default -> null;
|
||||
};
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable PsiElement getIdentifier(@Nullable PsiReferenceExpression expression) {
|
||||
if (expression == null) return null;
|
||||
PsiIdentifier[] identifiers = PsiTreeUtil.getChildrenOfType(expression, PsiIdentifier.class);
|
||||
if (identifiers == null || identifiers.length != 1) {
|
||||
return null;
|
||||
}
|
||||
return identifiers[0];
|
||||
}
|
||||
|
||||
private static @Nullable String getNameOfChronoEnum(@Nullable PsiExpression expression, @Nullable String methodName) {
|
||||
if (expression == null || methodName == null) return null;
|
||||
if (!(expression instanceof PsiReferenceExpression referenceExpression)) {
|
||||
return null;
|
||||
}
|
||||
PsiElement resolvedElement = referenceExpression.resolve();
|
||||
if (!(resolvedElement instanceof PsiEnumConstant enumConstant)) {
|
||||
return null;
|
||||
}
|
||||
PsiClass containingClass = enumConstant.getContainingClass();
|
||||
if (containingClass == null || !containingClass.isEnum()) {
|
||||
return null;
|
||||
}
|
||||
String classQualifiedName = containingClass.getQualifiedName();
|
||||
if (!(ChronoUtil.CHRONO_FIELD.equals(classQualifiedName) && (methodName.equals("get") || methodName.equals("with"))) &&
|
||||
!(ChronoUtil.CHRONO_UNIT.equals(classQualifiedName)) && (methodName.equals("plus") || methodName.equals("minus"))) {
|
||||
return null;
|
||||
}
|
||||
return enumConstant.getName();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class InlineChronoEnumCallFix extends PsiUpdateModCommandQuickFix {
|
||||
private final @NotNull String myNewMethodName;
|
||||
private final int myDeletedArgumentIndex;
|
||||
|
||||
InlineChronoEnumCallFix(@NotNull @NlsSafe String newMethodName, int deletedArgumentIndex) {
|
||||
myNewMethodName = newMethodName;
|
||||
myDeletedArgumentIndex = deletedArgumentIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x.call", myNewMethodName + "()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.explicit.chrono.field.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression == null) {
|
||||
return;
|
||||
}
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String text = ct.text(qualifierExpression) + "." + myNewMethodName;
|
||||
PsiExpression[] expressions = call.getArgumentList().getExpressions();
|
||||
StringJoiner joiner = new StringJoiner(",", "(", ")");
|
||||
for (int i = 0; i < expressions.length; i++) {
|
||||
if (i == myDeletedArgumentIndex) {
|
||||
continue;
|
||||
}
|
||||
joiner.add(ct.text(expressions[i]));
|
||||
}
|
||||
text += joiner.toString();
|
||||
ct.replaceAndRestoreComments(call, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.ex.InspectionElementsMergerBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class RedundantJavaTimeOperationMerger extends InspectionElementsMergerBase {
|
||||
|
||||
@Override
|
||||
public @NotNull String getMergedToolName() {
|
||||
return "RedundantJavaTimeOperations";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getSourceToolNames() {
|
||||
return new String[] {
|
||||
"RedundantCreationJavaTime", "RedundantCompareToJavaTime", "RedundantExplicitChronoField"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getSuppressIds() {
|
||||
return new String[] {
|
||||
"RedundantCreationJavaTime", "RedundantCompareToJavaTime", "RedundantExplicitChronoField"
|
||||
};
|
||||
}
|
||||
}
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool;
|
||||
import com.intellij.codeInspection.CommonQuickFixBundle;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.codeInspection.util.ChronoUtil;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.modcommand.ModCommandQuickFix;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.*;
|
||||
|
||||
public class RedundantJavaTimeOperationsInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
|
||||
private static final CallMatcher FROM_MATCHER = CallMatcher.anyOf(
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_TIME, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE_TIME, "from")
|
||||
.parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_OFFSET_DATE_TIME, "from")
|
||||
.parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_OFFSET_TIME, "from").parameterCount(1),
|
||||
CallMatcher.staticCall(JAVA_TIME_ZONED_DATE_TIME, "from")
|
||||
.parameterCount(1));
|
||||
|
||||
private static final CallMatcher LOCAL_DATE_OF_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "of").parameterTypes("int", "int", "int"),
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_DATE, "of").parameterTypes("int", "java.time.Month", "int"));
|
||||
|
||||
private static final CallMatcher GET_YEAR_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getYear").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getYear").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MONTH_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMonth").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MONTH_VALUE_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMonthValue").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMonthValue").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_DAY_OF_MONTH_MATCHER =
|
||||
CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getDayOfMonth").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getDayOfMonth").parameterCount(0));
|
||||
|
||||
private static final CallMatcher LOCAL_TIME_OF_MATCHER =
|
||||
CallMatcher.staticCall(JAVA_TIME_LOCAL_TIME, "of").parameterTypes("int", "int", "int", "int");
|
||||
|
||||
private static final CallMatcher GET_HOUR_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getHour").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getHour").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_MINUTE_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getMinute").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getMinute").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_SECOND_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getSecond").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getSecond").parameterCount(0));
|
||||
|
||||
private static final CallMatcher GET_NANO_MATCHER =
|
||||
CallMatcher.anyOf(CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "getNano").parameterCount(0),
|
||||
CallMatcher.instanceCall(JAVA_TIME_ZONED_DATE_TIME, "getNano").parameterCount(0));
|
||||
private static final String TO_LOCAL_TIME = "toLocalTime";
|
||||
private static final String TO_LOCAL_DATE = "toLocalDate";
|
||||
|
||||
private static final CallMatcher CAN_BE_SIMPLIFIED_MATCHERS = CallMatcher.anyOf(
|
||||
ChronoUtil.CHRONO_GET_MATCHERS,
|
||||
ChronoUtil.CHRONO_WITH_MATCHERS,
|
||||
ChronoUtil.CHRONO_PLUS_MINUS_MATCHERS
|
||||
);
|
||||
|
||||
private static final CallMatcher COMPARE_TO_METHODS = CallMatcher.anyOf(
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE, "compareTo").parameterTypes("java.time.chrono.ChronoLocalDate"),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_TIME, "compareTo").parameterTypes(JAVA_TIME_LOCAL_TIME),
|
||||
CallMatcher.instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "compareTo")
|
||||
.parameterTypes("java.time.chrono.ChronoLocalDateTime"),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_TIME, "compareTo").parameterTypes(JAVA_TIME_OFFSET_TIME),
|
||||
CallMatcher.instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "compareTo")
|
||||
.parameterTypes(JAVA_TIME_OFFSET_DATE_TIME)
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@SuppressWarnings("UnnecessaryReturnStatement")
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) {
|
||||
if (fixRedundantFrom(call)) return;
|
||||
if (fixRedundantOfLocalDate(call)) return;
|
||||
if (fixRedundantOfLocalTime(call)) return;
|
||||
if (fixRedundantExplicitChronoField(call)) return;
|
||||
if (fixRedundantComparison(call)) return;
|
||||
}
|
||||
|
||||
private boolean fixRedundantComparison(@NotNull PsiMethodCallExpression call) {
|
||||
if (!COMPARE_TO_METHODS.test(call)) return false;
|
||||
PsiElement nameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (nameElement == null) return false;
|
||||
|
||||
final PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression == null) {
|
||||
return false;
|
||||
}
|
||||
PsiType[] types = call.getArgumentList().getExpressionTypes();
|
||||
if (types.length != 1) {
|
||||
return false;
|
||||
}
|
||||
final PsiType argumentType = types[0];
|
||||
if (argumentType == null || !argumentType.equals(qualifierExpression.getType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiBinaryExpression binOp = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiBinaryExpression.class);
|
||||
if (binOp == null) return false;
|
||||
RelationType relationType = DfaPsiUtil.getRelationByToken(binOp.getOperationTokenType());
|
||||
if (relationType == RelationType.IS || relationType == RelationType.IS_NOT) return false;
|
||||
if (relationType == null) return false;
|
||||
if (ExpressionUtils.isZero(binOp.getLOperand())) {
|
||||
relationType = relationType.getFlipped();
|
||||
if (relationType == null) return false;
|
||||
}
|
||||
else if (!ExpressionUtils.isZero(binOp.getROperand())) {
|
||||
return false;
|
||||
}
|
||||
holder.registerProblem(nameElement,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.compare.java.time.problem.descriptor"),
|
||||
new InlineCompareToTimeCallFix(relationType, argumentType.getCanonicalText()));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean fixRedundantExplicitChronoField(@NotNull PsiMethodCallExpression call) {
|
||||
String methodName = call.getMethodExpression().getReferenceName();
|
||||
if (!"get".equals(methodName) && !"with".equals(methodName) &&
|
||||
!"plus".equals(methodName) && !"minus".equals(methodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CAN_BE_SIMPLIFIED_MATCHERS.matches(call)) return false;
|
||||
int fieldArgumentIndex = 1;
|
||||
if ("get".equals(methodName) || "with".equals(methodName)) {
|
||||
fieldArgumentIndex = 0;
|
||||
}
|
||||
PsiExpression[] expressions = call.getArgumentList().getExpressions();
|
||||
if (expressions.length < fieldArgumentIndex + 1) {
|
||||
return false;
|
||||
}
|
||||
@Nullable PsiExpression fieldExpression = expressions[fieldArgumentIndex];
|
||||
String chronoEnumName = getNameOfChronoEnum(fieldExpression, methodName);
|
||||
if (chronoEnumName == null) return false;
|
||||
String newMethodName = getNewMethodName(chronoEnumName, call);
|
||||
if (newMethodName == null) return false;
|
||||
PsiElement identifier = getIdentifier(call.getMethodExpression());
|
||||
if (identifier == null) return false;
|
||||
holder.registerProblem(identifier,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.explicit.chrono.field.problem.descriptor"),
|
||||
new InlineChronoEnumCallFix(newMethodName, fieldArgumentIndex));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static @Nullable String getNewMethodName(@NotNull String chronoEnumName, @NotNull PsiMethodCallExpression call) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isAvailableCall(method, chronoEnumName)) {
|
||||
return null;
|
||||
}
|
||||
//'with(ChronoField, long)' can be converted only to 'with...(int)'
|
||||
if ("with".equals(method.getName())) {
|
||||
PsiType[] types = call.getArgumentList().getExpressionTypes();
|
||||
if (types.length != 2 || types[1] == null || TypeConversionUtil.getTypeRank(types[1]) > TypeConversionUtil.INT_RANK) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String methodName = method.getName();
|
||||
return findEquivalentMethod(chronoEnumName, methodName);
|
||||
}
|
||||
|
||||
private static boolean isAvailableCall(@NotNull PsiMethod method, @NotNull String chronoEnumName) {
|
||||
return switch (method.getName()) {
|
||||
case "get" -> ChronoUtil.isAnyGetSupported(method, ChronoUtil.getChronoField(chronoEnumName));
|
||||
case "with" -> ChronoUtil.isWithSupported(method, ChronoUtil.getChronoField(chronoEnumName));
|
||||
case "plus", "minus" -> ChronoUtil.isPlusMinusSupported(method, ChronoUtil.getChronoUnit(chronoEnumName));
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable String findEquivalentMethod(@NotNull String chronoEnumName, @NotNull String methodName) {
|
||||
return switch (methodName) {
|
||||
case "plus" -> switch (chronoEnumName) {
|
||||
case "NANOS" -> "plusNanos";
|
||||
case "SECONDS" -> "plusSeconds";
|
||||
case "MINUTES" -> "plusMinutes";
|
||||
case "HOURS" -> "plusHours";
|
||||
case "DAYS" -> "plusDays";
|
||||
case "WEEKS" -> "plusWeeks";
|
||||
case "MONTHS" -> "plusMonths";
|
||||
case "YEARS" -> "plusYears";
|
||||
default -> null;
|
||||
};
|
||||
case "minus" -> switch (chronoEnumName) {
|
||||
case "NANOS" -> "minusNanos";
|
||||
case "SECONDS" -> "minusSeconds";
|
||||
case "MINUTES" -> "minusMinutes";
|
||||
case "HOURS" -> "minusHours";
|
||||
case "DAYS" -> "minusDays";
|
||||
case "WEEKS" -> "minusWeeks";
|
||||
case "MONTHS" -> "minusMonths";
|
||||
case "YEARS" -> "minusYears";
|
||||
default -> null;
|
||||
};
|
||||
case "get" -> switch (chronoEnumName) {
|
||||
case "NANO_OF_SECOND" -> "getNano";
|
||||
case "SECOND_OF_MINUTE" -> "getSecond";
|
||||
case "MINUTE_OF_HOUR" -> "getMinute";
|
||||
case "HOUR_OF_DAY" -> "getHour";
|
||||
case "DAY_OF_MONTH" -> "getDayOfMonth";
|
||||
case "DAY_OF_YEAR" -> "getDayOfYear";
|
||||
case "MONTH_OF_YEAR" -> "getMonth";
|
||||
case "YEAR" -> "getYear";
|
||||
default -> null;
|
||||
};
|
||||
case "with" -> switch (chronoEnumName) {
|
||||
case "NANO_OF_SECOND" -> "withNano";
|
||||
case "SECOND_OF_MINUTE" -> "withSecond";
|
||||
case "MINUTE_OF_HOUR" -> "withMinute";
|
||||
case "HOUR_OF_DAY" -> "withHour";
|
||||
case "DAY_OF_MONTH" -> "withDayOfMonth";
|
||||
case "DAY_OF_YEAR" -> "withDayOfYear";
|
||||
case "MONTH_OF_YEAR" -> "withMonth";
|
||||
case "YEAR" -> "withYear";
|
||||
default -> null;
|
||||
};
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static @Nullable PsiElement getIdentifier(@Nullable PsiReferenceExpression expression) {
|
||||
if (expression == null) return null;
|
||||
PsiIdentifier[] identifiers = PsiTreeUtil.getChildrenOfType(expression, PsiIdentifier.class);
|
||||
if (identifiers == null || identifiers.length != 1) {
|
||||
return null;
|
||||
}
|
||||
return identifiers[0];
|
||||
}
|
||||
|
||||
private static @Nullable String getNameOfChronoEnum(@Nullable PsiExpression expression, @Nullable String methodName) {
|
||||
if (expression == null || methodName == null) return null;
|
||||
if (!(expression instanceof PsiReferenceExpression referenceExpression)) {
|
||||
return null;
|
||||
}
|
||||
PsiElement resolvedElement = referenceExpression.resolve();
|
||||
if (!(resolvedElement instanceof PsiEnumConstant enumConstant)) {
|
||||
return null;
|
||||
}
|
||||
PsiClass containingClass = enumConstant.getContainingClass();
|
||||
if (containingClass == null || !containingClass.isEnum()) {
|
||||
return null;
|
||||
}
|
||||
String classQualifiedName = containingClass.getQualifiedName();
|
||||
if (!(ChronoUtil.CHRONO_FIELD.equals(classQualifiedName) && (methodName.equals("get") || methodName.equals("with"))) &&
|
||||
!(ChronoUtil.CHRONO_UNIT.equals(classQualifiedName)) && (methodName.equals("plus") || methodName.equals("minus"))) {
|
||||
return null;
|
||||
}
|
||||
return enumConstant.getName();
|
||||
}
|
||||
|
||||
private boolean fixRedundantOfLocalTime(@NotNull PsiMethodCallExpression call) {
|
||||
if (!LOCAL_TIME_OF_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 4) return false;
|
||||
if (!(arguments[0] instanceof PsiMethodCallExpression firstArgumentCall)) return false;
|
||||
if (!GET_HOUR_MATCHER.test(firstArgumentCall)) return false;
|
||||
if (!(arguments[1] instanceof PsiMethodCallExpression secondArgumentCall)) return false;
|
||||
if (!GET_MINUTE_MATCHER.test(secondArgumentCall)) return false;
|
||||
if (!(arguments[2] instanceof PsiMethodCallExpression thirdArgumentCall)) return false;
|
||||
if (!GET_SECOND_MATCHER.test(thirdArgumentCall)) return false;
|
||||
if (!(arguments[3] instanceof PsiMethodCallExpression fourthArgumentCall)) return false;
|
||||
if (!GET_NANO_MATCHER.test(fourthArgumentCall)) return false;
|
||||
PsiExpression expression1 = firstArgumentCall.getMethodExpression().getQualifierExpression();
|
||||
if (!(PsiUtil.skipParenthesizedExprDown(expression1) instanceof PsiReferenceExpression referenceExpression1 &&
|
||||
referenceExpression1.resolve() instanceof PsiVariable variable1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(areSameVariableReferences(holder.getProject(),
|
||||
expression1,
|
||||
secondArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
thirdArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
fourthArgumentCall.getMethodExpression().getQualifierExpression()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass variableClass = PsiUtil.resolveClassInClassTypeOnly(variable1.getType());
|
||||
if (variableClass == null) return false;
|
||||
PsiElement referenceNameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (referenceNameElement == null) return false;
|
||||
|
||||
PsiIdentifier identifier = variable1.getNameIdentifier();
|
||||
if (identifier == null) return false;
|
||||
if (JAVA_TIME_LOCAL_TIME.equals(variableClass.getQualifiedName())) {
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.error.message", "LocalTime"),
|
||||
RedundantCreationFix.create(identifier.getText(), RedundantCreationFix.FixType.REMOVE,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.creation.java.time.error.remove.fix.message",
|
||||
"LocalTime.of()")));
|
||||
return true;
|
||||
}
|
||||
PsiMethod[] localTimes = variableClass.findMethodsByName(TO_LOCAL_TIME, false);
|
||||
if (localTimes.length != 1) return false;
|
||||
|
||||
String newText = identifier.getText() + "." + TO_LOCAL_TIME + "()";
|
||||
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.error.message", "LocalTime"),
|
||||
RedundantCreationFix.create(newText, RedundantCreationFix.FixType.SIMPLIFY,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.creation.java.time.error.replace.fix.message",
|
||||
TO_LOCAL_TIME + "()")));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean fixRedundantOfLocalDate(@NotNull PsiMethodCallExpression call) {
|
||||
if (!LOCAL_DATE_OF_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 3) return false;
|
||||
if (!(arguments[0] instanceof PsiMethodCallExpression firstArgumentCall)) return false;
|
||||
if (!GET_YEAR_MATCHER.test(firstArgumentCall)) return false;
|
||||
if (!(arguments[1] instanceof PsiMethodCallExpression secondArgumentCall)) return false;
|
||||
if (!GET_MONTH_VALUE_MATCHER.test(secondArgumentCall) && !GET_MONTH_MATCHER.test(secondArgumentCall)) return false;
|
||||
if (!(arguments[2] instanceof PsiMethodCallExpression thirdArgumentCall)) return false;
|
||||
if (!GET_DAY_OF_MONTH_MATCHER.test(thirdArgumentCall)) return false;
|
||||
|
||||
PsiExpression firstArgument = PsiUtil.skipParenthesizedExprDown(firstArgumentCall.getMethodExpression().getQualifierExpression());
|
||||
if (!(firstArgument instanceof PsiReferenceExpression referenceExpression &&
|
||||
referenceExpression.resolve() instanceof PsiVariable firstVariable)) {
|
||||
return false;
|
||||
}
|
||||
if (!areSameVariableReferences(holder.getProject(),
|
||||
firstArgument,
|
||||
secondArgumentCall.getMethodExpression().getQualifierExpression(),
|
||||
thirdArgumentCall.getMethodExpression().getQualifierExpression())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass variableClass = PsiUtil.resolveClassInClassTypeOnly(firstArgument.getType());
|
||||
if (variableClass == null) return false;
|
||||
PsiElement referenceNameElement = call.getMethodExpression().getReferenceNameElement();
|
||||
if (referenceNameElement == null) return false;
|
||||
PsiIdentifier identifier = firstVariable.getNameIdentifier();
|
||||
if (identifier == null) return false;
|
||||
|
||||
if (JAVA_TIME_LOCAL_DATE.equals(variableClass.getQualifiedName())) {
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.error.message", "LocalDate"),
|
||||
RedundantCreationFix.create(identifier.getText(),
|
||||
RedundantCreationFix.FixType.REMOVE,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.creation.java.time.error.remove.fix.message",
|
||||
"LocalDate.of()")));
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiMethod[] localDates = variableClass.findMethodsByName(TO_LOCAL_DATE, false);
|
||||
if (localDates.length != 1) return false;
|
||||
String newText = identifier.getText() + "." + TO_LOCAL_DATE + "()";
|
||||
|
||||
holder.registerProblem(referenceNameElement,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.error.message", "LocalDate"),
|
||||
RedundantCreationFix.create(newText, RedundantCreationFix.FixType.SIMPLIFY,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.creation.java.time.error.replace.fix.message",
|
||||
TO_LOCAL_DATE + "()")));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean areSameVariableReferences(@NotNull Project project, PsiExpression... expressions) {
|
||||
if (expressions.length == 0) return false;
|
||||
PsiManager psiManager = PsiManager.getInstance(project);
|
||||
PsiVariable firstVariable = resolveVariable(expressions[0]);
|
||||
if (firstVariable == null) return false;
|
||||
for (PsiExpression expression : expressions) {
|
||||
PsiVariable variable = resolveVariable(expression);
|
||||
if (variable == null || !psiManager.areElementsEquivalent(firstVariable, variable)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiVariable resolveVariable(@Nullable PsiExpression expression) {
|
||||
if (expression == null) return null;
|
||||
PsiExpression unwrappedExpression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (!(unwrappedExpression instanceof PsiReferenceExpression referenceExpression)) return null;
|
||||
PsiElement resolvedElement = referenceExpression.resolve();
|
||||
return (resolvedElement instanceof PsiVariable variable) ? variable : null;
|
||||
}
|
||||
|
||||
private boolean fixRedundantFrom(@NotNull PsiMethodCallExpression call) {
|
||||
if (!FROM_MATCHER.test(call)) return false;
|
||||
PsiExpression[] arguments = call.getArgumentList().getExpressions();
|
||||
if (arguments.length != 1) return false;
|
||||
PsiExpression expression = arguments[0];
|
||||
PsiClass classOfArgument = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) return false;
|
||||
PsiClass classOfMethod = method.getContainingClass();
|
||||
if (classOfMethod == null) return false;
|
||||
PsiManager manager = classOfMethod.getManager();
|
||||
if (!manager.areElementsEquivalent(classOfArgument, classOfMethod)) return false;
|
||||
String newText = expression.getText();
|
||||
if (newText == null) return false;
|
||||
PsiElement identifier = call.getMethodExpression().getReferenceNameElement();
|
||||
if (identifier == null) return false;
|
||||
String className = classOfMethod.getName();
|
||||
if (className == null) return false;
|
||||
holder.registerProblem(identifier,
|
||||
InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.redundant.call.message",
|
||||
className + ".from()"),
|
||||
RedundantCreationFix.create(newText, RedundantCreationFix.FixType.REMOVE,
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.java.time.operation.creation.java.time.error.remove.fix.message",
|
||||
className + ".from()")));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class InlineChronoEnumCallFix extends PsiUpdateModCommandQuickFix {
|
||||
private final @NotNull String myNewMethodName;
|
||||
private final int myDeletedArgumentIndex;
|
||||
|
||||
InlineChronoEnumCallFix(@NotNull @NlsSafe String newMethodName, int deletedArgumentIndex) {
|
||||
myNewMethodName = newMethodName;
|
||||
myDeletedArgumentIndex = deletedArgumentIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x.call", myNewMethodName + "()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.explicit.chrono.field.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression == null) {
|
||||
return;
|
||||
}
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String text = ct.text(qualifierExpression) + "." + myNewMethodName;
|
||||
PsiExpression[] expressions = call.getArgumentList().getExpressions();
|
||||
StringJoiner joiner = new StringJoiner(",", "(", ")");
|
||||
for (int i = 0; i < expressions.length; i++) {
|
||||
if (i == myDeletedArgumentIndex) {
|
||||
continue;
|
||||
}
|
||||
joiner.add(ct.text(expressions[i]));
|
||||
}
|
||||
text += joiner.toString();
|
||||
ct.replaceAndRestoreComments(call, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RedundantCreationFix extends PsiUpdateModCommandQuickFix {
|
||||
private enum FixType {
|
||||
REMOVE, SIMPLIFY
|
||||
}
|
||||
|
||||
@NotNull private final String myNewText;
|
||||
@NotNull private final RedundantCreationFix.FixType myFixType;
|
||||
@IntentionName @Nullable private final String myMessageError;
|
||||
|
||||
private RedundantCreationFix(@NotNull String text, @NotNull RedundantCreationFix.FixType type,
|
||||
@IntentionName @Nullable String messageError) {
|
||||
myNewText = text;
|
||||
myFixType = type;
|
||||
myMessageError = messageError;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class, false);
|
||||
if (callExpression == null) return;
|
||||
new CommentTracker().replaceAndRestoreComments(callExpression, myNewText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
if (myMessageError != null) return myMessageError;
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return switch (myFixType) {
|
||||
case SIMPLIFY -> InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.family.name");
|
||||
case REMOVE -> InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.creation.java.time.remove.family.name");
|
||||
};
|
||||
}
|
||||
|
||||
private static @NotNull ModCommandQuickFix create(@NotNull String newText, @NotNull RedundantCreationFix.FixType type,
|
||||
@IntentionName @Nullable String messageError) {
|
||||
return new RedundantCreationFix(newText, type, messageError);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InlineCompareToTimeCallFix extends PsiUpdateModCommandQuickFix {
|
||||
private final @NotNull RelationType myRelationType;
|
||||
private final @NotNull String myArgumentType;
|
||||
|
||||
InlineCompareToTimeCallFix(@NotNull RelationType relationType, @NotNull @NlsSafe String argumentType) {
|
||||
myRelationType = relationType;
|
||||
myArgumentType = argumentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
String method = getMethodName();
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x.call", method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("inspection.redundant.java.time.operation.compare.java.time.family.name");
|
||||
}
|
||||
|
||||
private @NotNull String getMethodName() {
|
||||
return switch (myRelationType) {
|
||||
case EQ, NE -> myArgumentType.equals(JAVA_TIME_LOCAL_TIME) ? "equals" : "isEqual";
|
||||
case GT, LE -> "isAfter";
|
||||
case LT, GE -> "isBefore";
|
||||
default -> throw new UnsupportedOperationException(myRelationType.toString());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
if (call == null) return;
|
||||
PsiExpression first = call.getMethodExpression().getQualifierExpression();
|
||||
if (first == null) {
|
||||
return;
|
||||
}
|
||||
PsiExpression second = call.getArgumentList().getExpressions()[0];
|
||||
CommentTracker ct = new CommentTracker();
|
||||
String text = ct.text(first) + "." + getMethodName() + "(" + ct.text(second) + ")";
|
||||
|
||||
if (myRelationType == RelationType.NE || myRelationType == RelationType.LE || myRelationType == RelationType.GE) {
|
||||
text = "!" + text;
|
||||
}
|
||||
PsiBinaryExpression parent = PsiTreeUtil.getParentOfType(call, PsiBinaryExpression.class);
|
||||
if (parent == null) return;
|
||||
ct.replaceAndRestoreComments(parent, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports <code>java.time</code> comparisons with <code>compareTo()</code> calls that can be replaced with
|
||||
<code>isAfter()</code>, <code>isBefore()</code> or <code>isEqual()</code> calls.
|
||||
<p>Example:</p>
|
||||
<pre><code>
|
||||
LocalDate date1 = LocalDate.now();
|
||||
LocalDate date2 = LocalDate.now();
|
||||
boolean t = date1.compareTo(date2) > 0;
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalDate date1 = LocalDate.now();
|
||||
LocalDate date2 = LocalDate.now();
|
||||
boolean t = date1.isAfter(date2);
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2022.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
<!-- Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports redundant creation of date/time objects using <code>java.time</code> classes
|
||||
when simpler method calls can be used or creation can be avoided.
|
||||
|
||||
<p>The main <code>java.date</code> classes are marked as <code>@jdk.internal.ValueBased</code>.
|
||||
Such creations should be avoided as these classes are designed to be used in a value-based manner.</p>
|
||||
|
||||
<p>Example:</p>
|
||||
<pre><code>
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return LocalDateTime.from(now);
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return now;
|
||||
</code></pre>
|
||||
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2024.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports <code>java.time</code> method calls with <code>java.time.temporal.ChronoField</code> and
|
||||
<code>java.time.temporal.ChronoUnit</code> as arguments when these calls can be replaced with
|
||||
calls of more specific methods.
|
||||
<p>Example:</p>
|
||||
<pre><code>
|
||||
LocalTime localTime = LocalTime.now();
|
||||
int minute = localTime.get(ChronoField.MINUTE_OF_HOUR);
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalTime localTime = LocalTime.now();
|
||||
int minute = localTime.getMinute();
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2023.2</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!-- Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports redundant operation on 'java.time' object redundant:
|
||||
<ul>
|
||||
<li>creation of date/time objects from the JDK <code>java.time</code> package
|
||||
when simpler method calls can be used or creation can be avoided.</li>
|
||||
<li><code>java.time</code> method calls with <code>java.time.temporal.ChronoField</code> and
|
||||
<code>java.time.temporal.ChronoUnit</code> as arguments when these calls can be replaced with
|
||||
calls of more specific methods.</li>
|
||||
<li><code>java.time</code> comparisons with <code>compareTo()</code> calls that can be replaced with
|
||||
<code>isAfter()</code>, <code>isBefore()</code> or <code>isEqual()</code> calls.</li>
|
||||
</ul>
|
||||
|
||||
<p>Examples:</p>
|
||||
- Before:
|
||||
<pre><code>
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return LocalDateTime.from(now);
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
return now;
|
||||
</code></pre>
|
||||
- Before:
|
||||
<pre><code>
|
||||
LocalTime localTime = LocalTime.now();
|
||||
int minute = localTime.get(ChronoField.MINUTE_OF_HOUR);
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalTime localTime = LocalTime.now();
|
||||
int minute = localTime.getMinute();
|
||||
</code></pre>
|
||||
- Before:
|
||||
<pre><code>
|
||||
LocalDate date1 = LocalDate.now();
|
||||
LocalDate date2 = LocalDate.now();
|
||||
boolean t = date1.compareTo(date2) > 0;
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
LocalDate date1 = LocalDate.now();
|
||||
LocalDate date2 = LocalDate.now();
|
||||
boolean t = date1.isAfter(date2);
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2024.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
import java.time.*;
|
||||
import java.time.chrono.*;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
LocalTime localTime = LocalTime.now();
|
||||
LocalTime localTime2 = LocalTime.now();
|
||||
boolean b = localTime.com<caret>pareTo(localTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = localTime.compareTo(localTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localTime.compareTo(localTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = localTime.compareTo(localTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localTime.compareTo(localTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = localTime.compareTo(localTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
b = 0 < localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 <= localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 > localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 >= localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 == localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 != localTime.compareTo(localTime2);
|
||||
System.out.println(b);
|
||||
|
||||
b = ((localTime).compareTo((localTime2)) != 0);
|
||||
System.out.println(b);
|
||||
|
||||
OffsetTime offsetTime = OffsetTime.now();
|
||||
OffsetTime offsetTime2 = OffsetTime.now();
|
||||
b = offsetTime.compareTo(offsetTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = (((offsetTime.compareTo(offsetTime2)))) > 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.compareTo(offsetTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.compareTo(offsetTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.compareTo(offsetTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.compareTo(offsetTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.compareTo(offsetTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
OffsetDateTime offsetDateTime = OffsetDateTime.now();
|
||||
OffsetDateTime offsetDateTime2 = OffsetDateTime.now();
|
||||
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.compareTo(offsetDateTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
LocalDate localDate = LocalDate.now();
|
||||
LocalDate localDate2 = LocalDate.now();
|
||||
b = localDate.compareTo(localDate2) > 0;
|
||||
System.out.println(b);
|
||||
b = localDate.compareTo(localDate2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localDate.compareTo(localDate2) < 0;
|
||||
System.out.println(b);
|
||||
b = localDate.compareTo(localDate2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localDate.compareTo(localDate2) == 0;
|
||||
System.out.println(b);
|
||||
b = localDate.compareTo(localDate2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
b = localDate.compareTo(new ChronoLocalDate() {}) > 0;
|
||||
|
||||
LocalDateTime localDateTime = LocalDateTime.now();
|
||||
LocalDateTime localDateTime2 = LocalDateTime.now();
|
||||
b = localDateTime.compareTo(localDateTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.compareTo(localDateTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.compareTo(localDateTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.compareTo(localDateTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.compareTo(localDateTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.compareTo(localDateTime2) != 0;
|
||||
System.out.println(b);
|
||||
b = /*test0*/localDateTime/*test1*/./*test2*/compareTo/*test3*/(/*test4*/localDateTime2/*test5*/) > 0;
|
||||
System.out.println(b);
|
||||
try {
|
||||
b = localDateTime.compareTo(new ChronoLocalDateTime<>() {}) > 0;
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
b = /*test0*/localDateTime/*test1*/./*test2*/compareTo/*test3*/(/*test4*/localDateTime2/*test5*/) > 0;
|
||||
}
|
||||
|
||||
public static boolean unresolvedType(LocalTime localTime) {
|
||||
return localTime.compareTo(unresolved) > 0;
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import java.time.*;
|
||||
|
||||
class Main {
|
||||
|
||||
LocalTime convert(LocalTime source)
|
||||
{
|
||||
return LocalTime.from<caret>(source);
|
||||
}
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
|
||||
class Main {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import java.time.*;
|
||||
|
||||
public class DateRedundant {
|
||||
class DateRedundant {
|
||||
class First {
|
||||
LocalDateTime convert(LocalDateTime source)
|
||||
{
|
||||
+1
-1
@@ -5,6 +5,6 @@ import java.time.temporal.ChronoField;
|
||||
class Main {
|
||||
|
||||
public static int test(OffsetDateTime offsetDateTime) {
|
||||
return offsetDateTime.get<caret>(ChronoField.NANO_OF_SECOND);
|
||||
return offsetDateTime.<warning descr="Calls with explicit 'ChronoField' or 'ChronoUnit' arguments call can be simplified">get<caret></warning>(ChronoField.NANO_OF_SECOND);
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
import java.time.*;
|
||||
import java.time.chrono.*;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
LocalTime localTime = LocalTime.now();
|
||||
LocalTime localTime2 = LocalTime.now();
|
||||
boolean b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo<caret></warning>(localTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
b = 0 < localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 <= localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 > localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 >= localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 == localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
b = 0 != localTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localTime2);
|
||||
System.out.println(b);
|
||||
|
||||
b = ((localTime).<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>((localTime2)) != 0);
|
||||
System.out.println(b);
|
||||
|
||||
OffsetTime offsetTime = OffsetTime.now();
|
||||
OffsetTime offsetTime2 = OffsetTime.now();
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = (((offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2)))) > 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = offsetTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
OffsetDateTime offsetDateTime = OffsetDateTime.now();
|
||||
OffsetDateTime offsetDateTime2 = OffsetDateTime.now();
|
||||
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = offsetDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(offsetDateTime2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
LocalDate localDate = LocalDate.now();
|
||||
LocalDate localDate2 = LocalDate.now();
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) > 0;
|
||||
System.out.println(b);
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) < 0;
|
||||
System.out.println(b);
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) == 0;
|
||||
System.out.println(b);
|
||||
b = localDate.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDate2) != 0;
|
||||
System.out.println(b);
|
||||
|
||||
b = localDate.compareTo(new <error descr="Class 'Anonymous class derived from ChronoLocalDate' must implement abstract method 'getChronology()' in 'ChronoLocalDate'">ChronoLocalDate</error>() {}) > 0;
|
||||
|
||||
LocalDateTime localDateTime = LocalDateTime.now();
|
||||
LocalDateTime localDateTime2 = LocalDateTime.now();
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) > 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) >= 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) < 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) <= 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) == 0;
|
||||
System.out.println(b);
|
||||
b = localDateTime.<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>(localDateTime2) != 0;
|
||||
System.out.println(b);
|
||||
b = /*test0*/localDateTime/*test1*/./*test2*/<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>/*test3*/(/*test4*/localDateTime2/*test5*/) > 0;
|
||||
System.out.println(b);
|
||||
try {
|
||||
b = localDateTime.compareTo(new <error descr="Class 'Anonymous class derived from ChronoLocalDateTime' must implement abstract method 'toLocalDate()' in 'ChronoLocalDateTime'">ChronoLocalDateTime<></error>() {}) > 0;
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
b = /*test0*/localDateTime/*test1*/./*test2*/<warning descr="Expression with 'java.time' 'compareTo()' call can be simplified">compareTo</warning>/*test3*/(/*test4*/localDateTime2/*test5*/) > 0;
|
||||
}
|
||||
|
||||
public static boolean unresolvedType(LocalTime localTime) {
|
||||
return localTime.compareTo(<error descr="Cannot resolve symbol 'unresolved'">unresolved</error>) > 0;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import java.time.*;
|
||||
|
||||
class Main {
|
||||
|
||||
LocalTime convert(LocalTime source)
|
||||
{
|
||||
return LocalTime.<warning descr="Redundant 'LocalTime.from()' call">from<caret></warning>(source);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,19 +1,20 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.OffsetTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
|
||||
class Main {
|
||||
|
||||
public static int test(OffsetDateTime offsetDateTime) {
|
||||
return offsetDateTime.get<caret>(ChronoField.NANO_OF_SECOND);
|
||||
return offsetDateTime.<warning descr="Calls with explicit 'ChronoField' or 'ChronoUnit' arguments call can be simplified">get<caret></warning>(ChronoField.NANO_OF_SECOND);
|
||||
}
|
||||
public static int test2(OffsetDateTime offsetDateTime) {
|
||||
return offsetDateTime.get(ChronoField.INSTANT_SECONDS);
|
||||
}
|
||||
public static int test3(OffsetDateTime offsetDateTime) {
|
||||
return offsetDateTime.get(ChronoField.YEAR);
|
||||
return offsetDateTime.<warning descr="Calls with explicit 'ChronoField' or 'ChronoUnit' arguments call can be simplified">get</warning>(ChronoField.YEAR);
|
||||
}
|
||||
public static void test4(OffsetTime time) {
|
||||
time.get(1, ChronoUnit.YEARS);
|
||||
time.get(1, <error descr="Cannot resolve symbol 'ChronoUnit'">ChronoUnit</error>.YEARS);
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -1,35 +1,35 @@
|
||||
import java.time.*;
|
||||
|
||||
public class DateRedundant {
|
||||
class DateRedundant {
|
||||
class First {
|
||||
LocalDateTime convert(LocalDateTime source)
|
||||
{
|
||||
return LocalDateTime.from<caret>(source);
|
||||
return LocalDateTime.<warning descr="Redundant 'LocalDateTime.from()' call">from<caret></warning>(source);
|
||||
}
|
||||
|
||||
LocalTime convert(LocalTime source)
|
||||
{
|
||||
return LocalTime.from(source);
|
||||
return LocalTime.<warning descr="Redundant 'LocalTime.from()' call">from</warning>(source);
|
||||
}
|
||||
|
||||
LocalDate convert(LocalDate source)
|
||||
{
|
||||
return LocalDate.from(source);
|
||||
return LocalDate.<warning descr="Redundant 'LocalDate.from()' call">from</warning>(source);
|
||||
}
|
||||
|
||||
OffsetDateTime convert(OffsetDateTime source)
|
||||
{
|
||||
return OffsetDateTime.from(source);
|
||||
return OffsetDateTime.<warning descr="Redundant 'OffsetDateTime.from()' call">from</warning>(source);
|
||||
}
|
||||
|
||||
OffsetTime convert(OffsetTime source)
|
||||
{
|
||||
return OffsetTime.from(source);
|
||||
return OffsetTime.<warning descr="Redundant 'OffsetTime.from()' call">from</warning>(source);
|
||||
}
|
||||
|
||||
ZonedDateTime convert(ZonedDateTime source)
|
||||
{
|
||||
return ZonedDateTime.from(source);
|
||||
return ZonedDateTime.<warning descr="Redundant 'ZonedDateTime.from()' call">from</warning>(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class DateRedundant {
|
||||
static class Third {
|
||||
private LocalDate getLocalDate(LocalDate fullTime)
|
||||
{
|
||||
return LocalDate.of(fullTime.getYear(),
|
||||
return LocalDate.<warning descr="Redundant creation of 'LocalDate' object">of</warning>(fullTime.getYear(),
|
||||
fullTime.getMonth(),
|
||||
fullTime.getDayOfMonth()
|
||||
);
|
||||
@@ -45,7 +45,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalDate getLocalDate(ZonedDateTime fullTime)
|
||||
{
|
||||
return LocalDate.of(fullTime.getYear(),
|
||||
return LocalDate.<warning descr="Redundant creation of 'LocalDate' object">of</warning>(fullTime.getYear(),
|
||||
fullTime.getMonth(),
|
||||
fullTime.getDayOfMonth()
|
||||
);
|
||||
@@ -53,7 +53,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalTime getLocalTime(ZonedDateTime fullTime)
|
||||
{
|
||||
return LocalTime.of(fullTime.getHour(),
|
||||
return LocalTime.<warning descr="Redundant creation of 'LocalTime' object">of</warning>(fullTime.getHour(),
|
||||
fullTime.getMinute(),
|
||||
fullTime.getSecond(),
|
||||
fullTime.getNano());
|
||||
@@ -61,7 +61,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalDate getLocalDate(LocalDateTime fullTime)
|
||||
{
|
||||
return LocalDate.of(fullTime.getYear(),
|
||||
return LocalDate.<warning descr="Redundant creation of 'LocalDate' object">of</warning>(fullTime.getYear(),
|
||||
fullTime.getMonth(),
|
||||
fullTime.getDayOfMonth()
|
||||
);
|
||||
@@ -69,7 +69,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalDate getLocalDate2(LocalDateTime fullTime)
|
||||
{
|
||||
return LocalDate.of(fullTime.getYear(),
|
||||
return LocalDate.<warning descr="Redundant creation of 'LocalDate' object">of</warning>(fullTime.getYear(),
|
||||
fullTime.getMonthValue(),
|
||||
fullTime.getDayOfMonth()
|
||||
);
|
||||
@@ -77,7 +77,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalTime getLocalTime(LocalDateTime fullTime)
|
||||
{
|
||||
return LocalTime.of(fullTime.getHour(),
|
||||
return LocalTime.<warning descr="Redundant creation of 'LocalTime' object">of</warning>(fullTime.getHour(),
|
||||
fullTime.getMinute(),
|
||||
fullTime.getSecond(),
|
||||
fullTime.getNano());
|
||||
@@ -85,7 +85,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalDate getLocalDate(OffsetDateTime fullTime)
|
||||
{
|
||||
return LocalDate.of(fullTime.getYear(),
|
||||
return LocalDate.<warning descr="Redundant creation of 'LocalDate' object">of</warning>(fullTime.getYear(),
|
||||
fullTime.getMonthValue(),
|
||||
fullTime.getDayOfMonth()
|
||||
);
|
||||
@@ -94,7 +94,7 @@ public class DateRedundant {
|
||||
private LocalTime getLocalTime(OffsetDateTime fullTime)
|
||||
{
|
||||
return LocalTime.
|
||||
of(fullTime.getHour(),
|
||||
<warning descr="Redundant creation of 'LocalTime' object">of</warning>(fullTime.getHour(),
|
||||
fullTime.getMinute(),
|
||||
fullTime.getSecond(),
|
||||
fullTime.getNano());
|
||||
@@ -102,7 +102,7 @@ public class DateRedundant {
|
||||
|
||||
private LocalTime getLocalTime(LocalTime fullTime)
|
||||
{
|
||||
return LocalTime.of(fullTime.getHour(),
|
||||
return LocalTime.<warning descr="Redundant creation of 'LocalTime' object">of</warning>(fullTime.getHour(),
|
||||
fullTime.getMinute(),
|
||||
fullTime.getSecond(),
|
||||
fullTime.getNano());
|
||||
+3
-25
@@ -1,33 +1,11 @@
|
||||
// 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.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.RedundantCompareToJavaTimeInspection;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
//todo just to preserve place
|
||||
public class RedundantCompareToJavaTimeInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
}
|
||||
|
||||
public void testCompareTo() {
|
||||
final LocalInspectionTool inspection = new RedundantCompareToJavaTimeInspection();
|
||||
myFixture.enableInspections(inspection);
|
||||
|
||||
myFixture.configureByFile("beforeCompareTo.java");
|
||||
myFixture.launchAction(myFixture.findSingleIntention("Fix all 'Expression with 'java.time' 'compareTo()' call can be simplified' problems in file"));
|
||||
myFixture.checkResultByFile("afterCompareTo.java");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() +
|
||||
"/codeInsight/daemonCodeAnalyzer/quickFix/redundantCompareToJavaTime";
|
||||
public void testEmptyMethod() {
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.redundancy;
|
||||
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
public class RedundantExplicitChronoFieldInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/java/java-tests/testData/ig/com/siyeh/igtest/redundancy/redundant_explicit_chrono_field/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.enableInspections(new RedundantExplicitChronoFieldInspection());
|
||||
}
|
||||
|
||||
public void testPreview() {
|
||||
doTest("Replace with 'getNano()' call");
|
||||
}
|
||||
|
||||
public void testOffsetDateTimeConvert() {
|
||||
doTest(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
}
|
||||
|
||||
private void doTest(@Nullable String quickFixName) {
|
||||
myFixture.configureByFile(getTestDataPath() + "before" + getTestName(false) + ".java");
|
||||
if (quickFixName != null) {
|
||||
myFixture.checkPreviewAndLaunchAction(myFixture.findSingleIntention(quickFixName));
|
||||
}
|
||||
else {
|
||||
myFixture.launchAction(myFixture.findSingleIntention(InspectionsBundle.message("fix.all.inspection.problems.in.file",
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.explicit.chrono.field.display.name"))));
|
||||
}
|
||||
myFixture.checkResultByFile("after" + getTestName(false) + ".java");
|
||||
}
|
||||
}
|
||||
+19
-7
@@ -8,27 +8,38 @@ import com.siyeh.InspectionGadgetsBundle;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
public class RedundantCreationJavaTimeInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public class RedundantJavaTimeOperationInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/java/java-tests/testData/ig/com/siyeh/igtest/redundancy/redundant_creation_java_time/";
|
||||
return "/java/java-tests/testData/ig/com/siyeh/igtest/redundancy/redundant_java_time_operations/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.enableInspections(new RedundantCreationJavaTimeInspection());
|
||||
myFixture.enableInspections(new RedundantJavaTimeOperationsInspection());
|
||||
}
|
||||
|
||||
public void testPreview() {
|
||||
doTest("Simplify creation of 'java.time' object");
|
||||
public void testCreationPreview() {
|
||||
doTest("Remove 'LocalTime.from()' call");
|
||||
}
|
||||
|
||||
public void testRedundantCreationOfDateTime() {
|
||||
doTest(null);
|
||||
}
|
||||
|
||||
public void testChronoConverterPreview() {
|
||||
doTest("Replace with 'getNano()' call");
|
||||
}
|
||||
|
||||
public void testOffsetDateTimeConvert() {
|
||||
doTest(null);
|
||||
}
|
||||
|
||||
public void testCompareTo() {
|
||||
doTest(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
@@ -37,13 +48,14 @@ public class RedundantCreationJavaTimeInspectionTest extends LightJavaCodeInsigh
|
||||
|
||||
private void doTest(@Nullable String quickFixName) {
|
||||
myFixture.configureByFile(getTestDataPath() + "before" + getTestName(false) + ".java");
|
||||
myFixture.testHighlighting(true, false, true);
|
||||
if (quickFixName != null) {
|
||||
myFixture.checkPreviewAndLaunchAction(myFixture.findSingleIntention(quickFixName));
|
||||
}
|
||||
else {
|
||||
myFixture.launchAction(myFixture.findSingleIntention(InspectionsBundle.message("fix.all.inspection.problems.in.file",
|
||||
InspectionGadgetsBundle.message(
|
||||
"inspection.redundant.creation.java.time.display.name"))));
|
||||
"inspection.redundant.java.time.operation.display.name"))));
|
||||
}
|
||||
myFixture.checkResultByFile("after" + getTestName(false) + ".java");
|
||||
}
|
||||
Reference in New Issue
Block a user