[java-inspections] IDEA-313812. Support isAfter, isBefore for java.time in DFA

GitOrigin-RevId: e3ffce95ce6f888687642bbf462706bbeaadb56a
This commit is contained in:
Mikhail Pyltsin
2023-02-21 14:24:19 +00:00
committed by intellij-monorepo-bot
parent 86f22bd478
commit 9f26b86c19
3 changed files with 81 additions and 12 deletions
@@ -21,8 +21,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.codeInspection.dataFlow.ContractReturnValue.*;
import static com.intellij.codeInspection.dataFlow.MethodContract.singleConditionContract;
import static com.intellij.codeInspection.dataFlow.MethodContract.trivialContract;
import static com.intellij.codeInspection.dataFlow.MethodContract.*;
import static com.intellij.codeInspection.dataFlow.StandardMethodContract.ValueConstraint.*;
import static com.intellij.codeInspection.dataFlow.StandardMethodContract.createConstraintArray;
import static com.intellij.psi.CommonClassNames.*;
@@ -184,16 +183,31 @@ public final class HardcodedContracts {
.register(enumValues(), ContractProvider.of(StandardMethodContract.fromText("->new")))
.register(staticCall("java.lang.System", "arraycopy"), expression -> getArraycopyContract())
.register(anyOf(
instanceCall("java.util.Date", "before", "after"),
instanceCall("java.time.LocalDate", "isBefore", "isAfter"),
instanceCall("java.time.LocalDateTime", "isBefore", "isAfter"),
instanceCall("java.time.LocalTime", "isBefore", "isAfter"),
instanceCall("java.time.ZonedDateTime", "isBefore", "isAfter"),
instanceCall("java.time.Year", "isBefore", "isAfter"),
instanceCall("java.time.YearMonth", "isBefore", "isAfter")
), ContractProvider.of(
singleConditionContract(ContractValue.qualifier(), RelationType.EQ, ContractValue.argument(0), returnFalse())
))
instanceCall(JAVA_TIME_LOCAL_DATE, "isAfter"),
instanceCall(JAVA_TIME_LOCAL_TIME, "isAfter"),
instanceCall(JAVA_TIME_OFFSET_TIME, "isAfter"),
instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "isAfter"),
instanceCall(JAVA_TIME_ZONED_DATE_TIME, "isAfter"),
instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "isAfter"),
instanceCall("java.util.Date", "after"),
instanceCall("java.time.Year", "isAfter"),
instanceCall("java.time.YearMonth", "isAfter")),
ContractProvider.of(
singleConditionContract(ContractValue.qualifier(), RelationType.GT, ContractValue.argument(0), returnBoolean(true)),
trivialContract(returnBoolean(false))))
.register(anyOf(
instanceCall(JAVA_TIME_LOCAL_DATE, "isBefore"),
instanceCall(JAVA_TIME_LOCAL_TIME, "isBefore"),
instanceCall(JAVA_TIME_OFFSET_TIME, "isBefore"),
instanceCall(JAVA_TIME_OFFSET_DATE_TIME, "isBefore"),
instanceCall(JAVA_TIME_ZONED_DATE_TIME, "isBefore"),
instanceCall(JAVA_TIME_LOCAL_DATE_TIME, "isBefore"),
instanceCall("java.util.Date", "before"),
instanceCall("java.time.Year", "isBefore"),
instanceCall("java.time.YearMonth", "isBefore")),
ContractProvider.of(
singleConditionContract(ContractValue.qualifier(), RelationType.LT, ContractValue.argument(0), returnBoolean(true)),
trivialContract(returnBoolean(false))))
//for propagation CONSUMED_STREAM
.register(ConsumedStreamUtils.getSkipStreamMatchers(), ContractProvider.of(trivialContract(returnThis())))
.register(staticCall(JAVA_LANG_CHARACTER, "isSurrogate").parameterCount(1),
@@ -0,0 +1,30 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
public class DateTimeComparing {
public void testLocalDateTime() {
LocalDateTime now1 = LocalDateTime.now();
LocalDateTime now2 = LocalDateTime.now();
if (now1.isAfter(now2)) {
if (<warning descr="Condition 'now1.isBefore(now2)' is always 'false'">now1.isBefore(now2)</warning>) {
}
if (<warning descr="Condition 'now2.isBefore(now1)' is always 'true'">now2.isBefore(now1)</warning>) {
}
}
}
public void testLocalTime() {
LocalTime now1 = LocalTime.now();
LocalTime now2 = LocalTime.now();
if (now1.isAfter(now2)) {
if (<warning descr="Condition 'now1.isBefore(now2)' is always 'false'">now1.isBefore(now2)</warning>) {
}
if (<warning descr="Condition 'now2.isBefore(now1)' is always 'true'">now2.isBefore(now1)</warning>) {
}
}
}
}
@@ -184,4 +184,29 @@ public class HardcodedContractsTest extends DataFlowInspectionTestCase {
}
public void testCharacterMethods() { checkHighlighting(); }
@SuppressWarnings("MethodOverloadsMethodOfSuperclass")
public void testDateTimeComparing() {
myFixture.addClass("""
package java.time.chrono;
public interface ChronoLocalDateTime<T> { }""");
myFixture.addClass("""
package java.time;
import java.time.temporal.TemporalUnit;
public final class LocalTime {
public static LocalTime now() { return new LocalTime(); }
public boolean isBefore(LocalTime localTime) { return false; }
public boolean isAfter(LocalTime localTime) { return false; }
}""");
myFixture.addClass("""
package java.time;
import java.time.chrono.ChronoLocalDateTime;
import java.time.temporal.TemporalUnit;
public final class LocalDateTime implements ChronoLocalDateTime<LocalDate> {
public static LocalDateTime now() { return new LocalDateTime(); }
public boolean isBefore(ChronoLocalDateTime<LocalDate> localDateTime2) { return false; }
public boolean isAfter(ChronoLocalDateTime<LocalDate> localDateTime2) { return false; }
}""");
checkHighlighting();
}
}