diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnit.java b/core/src/main/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnit.java similarity index 67% rename from core/src/main/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnit.java rename to core/src/main/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnit.java index 09fa0977f4a..131c9de064d 100644 --- a/core/src/main/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnit.java +++ b/core/src/main/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnit.java @@ -20,6 +20,8 @@ import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; import static com.google.errorprone.bugpatterns.time.DurationGetTemporalUnit.getInvalidChronoUnit; import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.anyOf; +import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod; import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod; import com.google.common.collect.ImmutableSet; @@ -36,28 +38,36 @@ import java.util.Arrays; /** - * Bans calls to {@code Duration.of(long, TemporalUnit)} where the {@link - * java.time.temporal.TemporalUnit} is not {@link ChronoUnit#DAYS} or it has an estimated duration - * (which is guaranteed to throw an {@code DateTimeException}). + * Bans calls to {@code Duration} APIs where the {@link java.time.temporal.TemporalUnit} is not + * {@link ChronoUnit#DAYS} or it has an estimated duration (which is guaranteed to throw an {@code + * DateTimeException}). */ @BugPattern( - name = "DurationOfLongTemporalUnit", - summary = "Duration.of(long, TemporalUnit) only works for DAYS or exact durations.", + name = "DurationTemporalUnit", + summary = "Duration APIs only work for DAYS or exact durations.", explanation = - "Duration.of(long, TemporalUnit) only works for TemporalUnits with exact durations or" + "Duration APIs only work for TemporalUnits with exact durations or" + " ChronoUnit.DAYS. E.g., Duration.of(1, ChronoUnit.YEARS) is guaranteed to throw a" + " DateTimeException.", severity = ERROR, providesFix = NO_FIX) -public final class DurationOfLongTemporalUnit extends BugChecker - implements MethodInvocationTreeMatcher { +public final class DurationTemporalUnit extends BugChecker implements MethodInvocationTreeMatcher { + + private static final String DURATION = "java.time.Duration"; + private static final String TEMPORAL_UNIT = "java.time.temporal.TemporalUnit"; private static final Matcher DURATION_OF_LONG_TEMPORAL_UNIT = allOf( - staticMethod() - .onClass("java.time.Duration") - .named("of") - .withParameters("long", "java.time.temporal.TemporalUnit"), + anyOf( + staticMethod().onClass(DURATION).named("of").withParameters("long", TEMPORAL_UNIT), + instanceMethod() + .onExactClass(DURATION) + .named("minus") + .withParameters("long", TEMPORAL_UNIT), + instanceMethod() + .onExactClass(DURATION) + .named("plus") + .withParameters("long", TEMPORAL_UNIT)), Matchers.not(Matchers.packageStartsWith("java."))); private static final ImmutableSet INVALID_TEMPORAL_UNITS = @@ -69,15 +79,10 @@ public final class DurationOfLongTemporalUnit extends BugChecker @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { if (DURATION_OF_LONG_TEMPORAL_UNIT.matches(tree, state)) { - if (isSecondParamDaysOrExactDuration(tree)) { + if (getInvalidChronoUnit(tree.getArguments().get(1), INVALID_TEMPORAL_UNITS).isPresent()) { return describeMatch(tree); } } return Description.NO_MATCH; } - - // will be used by other checks (e.g., Instant.plus(long, TemporalUnit)) - static boolean isSecondParamDaysOrExactDuration(MethodInvocationTree tree) { - return getInvalidChronoUnit(tree.getArguments().get(1), INVALID_TEMPORAL_UNITS).isPresent(); - } } diff --git a/core/src/main/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnit.java b/core/src/main/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnit.java new file mode 100644 index 00000000000..97609ee310b --- /dev/null +++ b/core/src/main/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnit.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019 The Error Prone Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.errorprone.bugpatterns.time; + +import static com.google.common.collect.Sets.toImmutableEnumSet; +import static com.google.errorprone.BugPattern.ProvidesFix.NO_FIX; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.bugpatterns.time.DurationGetTemporalUnit.getInvalidChronoUnit; +import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.anyOf; +import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod; + +import com.google.common.collect.ImmutableSet; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.matchers.Matchers; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.MethodInvocationTree; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; + +/** + * Bans calls to {@code Instant} APIs where the {@link java.time.temporal.TemporalUnit} is not one + * of: {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS}, {@code MINUTES}, {@code + * HOURS}, {@code HALF_DAYS}, or {@code DAYS}. + */ +@BugPattern( + name = "InstantTemporalUnit", + summary = + "Instant APIs only work for NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, HALF_DAYS and" + + " DAYS.", + severity = ERROR, + providesFix = NO_FIX) +public final class InstantTemporalUnit extends BugChecker implements MethodInvocationTreeMatcher { + + private static final String INSTANT = "java.time.Instant"; + private static final String TEMPORAL_UNIT = "java.time.temporal.TemporalUnit"; + + private static final Matcher INSTANT_OF_LONG_TEMPORAL_UNIT = + allOf( + anyOf( + instanceMethod() + .onExactClass(INSTANT) + .named("minus") + .withParameters("long", TEMPORAL_UNIT), + instanceMethod() + .onExactClass(INSTANT) + .named("plus") + .withParameters("long", TEMPORAL_UNIT), + instanceMethod() + .onExactClass(INSTANT) + .named("until") + .withParameters("java.time.temporal.Temporal", TEMPORAL_UNIT)), + Matchers.not(Matchers.packageStartsWith("java."))); + + // This definition comes from Instant.isSupported(TemporalUnit) + static final ImmutableSet INVALID_TEMPORAL_UNITS = + Arrays.stream(ChronoUnit.values()) + .filter(c -> !c.isTimeBased()) + .filter(c -> !c.equals(ChronoUnit.DAYS)) // DAYS is explicitly allowed + .collect(toImmutableEnumSet()); + + @Override + public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { + if (INSTANT_OF_LONG_TEMPORAL_UNIT.matches(tree, state)) { + if (getInvalidChronoUnit(tree.getArguments().get(1), INVALID_TEMPORAL_UNITS).isPresent()) { + return describeMatch(tree); + } + } + return Description.NO_MATCH; + } +} diff --git a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java index acfb58fdbad..7a855826283 100644 --- a/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java +++ b/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java @@ -391,8 +391,9 @@ import com.google.errorprone.bugpatterns.threadsafety.UnlockMethodChecker; import com.google.errorprone.bugpatterns.time.DurationFrom; import com.google.errorprone.bugpatterns.time.DurationGetTemporalUnit; -import com.google.errorprone.bugpatterns.time.DurationOfLongTemporalUnit; +import com.google.errorprone.bugpatterns.time.DurationTemporalUnit; import com.google.errorprone.bugpatterns.time.DurationToLongTimeUnit; +import com.google.errorprone.bugpatterns.time.InstantTemporalUnit; import com.google.errorprone.bugpatterns.time.JavaDurationGetSecondsGetNano; import com.google.errorprone.bugpatterns.time.JavaDurationWithNanos; import com.google.errorprone.bugpatterns.time.JavaDurationWithSeconds; @@ -492,7 +493,7 @@ public static ScannerSupplier errorChecks() { DuplicateMapKeys.class, DurationFrom.class, DurationGetTemporalUnit.class, - DurationOfLongTemporalUnit.class, + DurationTemporalUnit.class, DurationToLongTimeUnit.class, EqualsHashCode.class, EqualsNaN.class, @@ -518,6 +519,7 @@ public static ScannerSupplier errorChecks() { InfiniteRecursion.class, InjectOnFinalField.class, InjectOnMemberAndConstructor.class, + InstantTemporalUnit.class, InvalidPatternSyntax.class, InvalidTimeZoneID.class, InvalidZoneId.class, diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnitTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnitTest.java similarity index 52% rename from core/src/test/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnitTest.java rename to core/src/test/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnitTest.java index a93acaafecd..38331b89658 100644 --- a/core/src/test/java/com/google/errorprone/bugpatterns/time/DurationOfLongTemporalUnitTest.java +++ b/core/src/test/java/com/google/errorprone/bugpatterns/time/DurationTemporalUnitTest.java @@ -20,11 +20,11 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Tests for {@link DurationOfLongTemporalUnit}. */ +/** Tests for {@link DurationTemporalUnit}. */ @RunWith(JUnit4.class) -public class DurationOfLongTemporalUnitTest { +public class DurationTemporalUnitTest { private final CompilationTestHelper helper = - CompilationTestHelper.newInstance(DurationOfLongTemporalUnit.class, getClass()); + CompilationTestHelper.newInstance(DurationTemporalUnit.class, getClass()); @Test public void durationOf_good() { @@ -54,26 +54,122 @@ public void durationOf_bad() { "import java.time.Duration;", "import java.time.temporal.ChronoUnit;", "public class TestClass {", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D0 = Duration.of(1, ChronoUnit.CENTURIES);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D1 = Duration.of(1, ChronoUnit.DECADES);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D2 = Duration.of(1, ChronoUnit.ERAS);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D3 = Duration.of(1, ChronoUnit.FOREVER);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D4 = Duration.of(1, ChronoUnit.MILLENNIA);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D5 = Duration.of(1, ChronoUnit.MONTHS);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D6 = Duration.of(1, ChronoUnit.WEEKS);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D7 = Duration.of(1, ChronoUnit.YEARS);", "}") .doTest(); } + @Test + public void durationPlus_good() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Duration;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " private static final Duration D0 = Duration.ZERO.plus(1, ChronoUnit.DAYS);", + " private static final Duration D1 = Duration.ZERO.plus(1, ChronoUnit.HALF_DAYS);", + " private static final Duration D2 = Duration.ZERO.plus(1, ChronoUnit.HOURS);", + " private static final Duration D3 = Duration.ZERO.plus(1, ChronoUnit.MICROS);", + " private static final Duration D4 = Duration.ZERO.plus(1, ChronoUnit.MILLIS);", + " private static final Duration D5 = Duration.ZERO.plus(1, ChronoUnit.MINUTES);", + " private static final Duration D6 = Duration.ZERO.plus(1, ChronoUnit.NANOS);", + " private static final Duration D7 = Duration.ZERO.plus(1, ChronoUnit.SECONDS);", + "}") + .doTest(); + } + + @Test + public void durationPlus_bad() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Duration;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D0 = Duration.ZERO.plus(1, ChronoUnit.CENTURIES);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D1 = Duration.ZERO.plus(1, ChronoUnit.DECADES);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D2 = Duration.ZERO.plus(1, ChronoUnit.ERAS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D3 = Duration.ZERO.plus(1, ChronoUnit.FOREVER);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D4 = Duration.ZERO.plus(1, ChronoUnit.MILLENNIA);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D5 = Duration.ZERO.plus(1, ChronoUnit.MONTHS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D6 = Duration.ZERO.plus(1, ChronoUnit.WEEKS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D7 = Duration.ZERO.plus(1, ChronoUnit.YEARS);", + "}") + .doTest(); + } + + @Test + public void durationMinus_good() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Duration;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " private static final Duration D0 = Duration.ZERO.minus(1, ChronoUnit.DAYS);", + " private static final Duration D1 = Duration.ZERO.minus(1, ChronoUnit.HALF_DAYS);", + " private static final Duration D2 = Duration.ZERO.minus(1, ChronoUnit.HOURS);", + " private static final Duration D3 = Duration.ZERO.minus(1, ChronoUnit.MICROS);", + " private static final Duration D4 = Duration.ZERO.minus(1, ChronoUnit.MILLIS);", + " private static final Duration D5 = Duration.ZERO.minus(1, ChronoUnit.MINUTES);", + " private static final Duration D6 = Duration.ZERO.minus(1, ChronoUnit.NANOS);", + " private static final Duration D7 = Duration.ZERO.minus(1, ChronoUnit.SECONDS);", + "}") + .doTest(); + } + + @Test + public void durationMinus_bad() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Duration;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D0 = Duration.ZERO.minus(1, ChronoUnit.CENTURIES);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D1 = Duration.ZERO.minus(1, ChronoUnit.DECADES);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D2 = Duration.ZERO.minus(1, ChronoUnit.ERAS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D3 = Duration.ZERO.minus(1, ChronoUnit.FOREVER);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D4 = Duration.ZERO.minus(1, ChronoUnit.MILLENNIA);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D5 = Duration.ZERO.minus(1, ChronoUnit.MONTHS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D6 = Duration.ZERO.minus(1, ChronoUnit.WEEKS);", + " // BUG: Diagnostic contains: DurationTemporalUnit", + " private static final Duration D7 = Duration.ZERO.minus(1, ChronoUnit.YEARS);", + "}") + .doTest(); + } + @Test public void durationOfStaticImport() { helper @@ -86,7 +182,7 @@ public void durationOfStaticImport() { "public class TestClass {", " private static final Duration D1 = Duration.of(1, SECONDS);", " private static final Duration D2 = Duration.of(1, NANOS);", - " // BUG: Diagnostic contains: DurationOfLongTemporalUnit", + " // BUG: Diagnostic contains: DurationTemporalUnit", " private static final Duration D3 = Duration.of(1, YEARS);", "}") .doTest(); diff --git a/core/src/test/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnitTest.java b/core/src/test/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnitTest.java new file mode 100644 index 00000000000..dfd27d9a27e --- /dev/null +++ b/core/src/test/java/com/google/errorprone/bugpatterns/time/InstantTemporalUnitTest.java @@ -0,0 +1,146 @@ +/* + * Copyright 2019 The Error Prone Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.errorprone.bugpatterns.time; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.errorprone.CompilationTestHelper; +import java.time.temporal.ChronoUnit; +import java.util.EnumSet; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link InstantTemporalUnit}. */ +@RunWith(JUnit4.class) +public final class InstantTemporalUnitTest { + private final CompilationTestHelper helper = + CompilationTestHelper.newInstance(InstantTemporalUnit.class, getClass()); + + @Test + public void invalidTemporalUnitsMatchesEnumeratedList() throws Exception { + // This list comes from the Instant javadocs, but the checker builds the list based on the + // definition in Instant.isSupported(): unit.isTimeBased() || unit == DAYS; + assertThat(InstantTemporalUnit.INVALID_TEMPORAL_UNITS) + .containsExactlyElementsIn( + EnumSet.complementOf( + EnumSet.of( + ChronoUnit.NANOS, + ChronoUnit.MICROS, + ChronoUnit.MILLIS, + ChronoUnit.SECONDS, + ChronoUnit.MINUTES, + ChronoUnit.HOURS, + ChronoUnit.HALF_DAYS, + ChronoUnit.DAYS))); + } + + @Test + public void instantPlus_good() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Instant;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " private static final Instant I0 = Instant.EPOCH.plus(1, ChronoUnit.DAYS);", + " private static final Instant I1 = Instant.EPOCH.plus(1, ChronoUnit.HALF_DAYS);", + " private static final Instant I2 = Instant.EPOCH.plus(1, ChronoUnit.HOURS);", + " private static final Instant I3 = Instant.EPOCH.plus(1, ChronoUnit.MICROS);", + " private static final Instant I4 = Instant.EPOCH.plus(1, ChronoUnit.MILLIS);", + " private static final Instant I5 = Instant.EPOCH.plus(1, ChronoUnit.MINUTES);", + " private static final Instant I6 = Instant.EPOCH.plus(1, ChronoUnit.NANOS);", + " private static final Instant I7 = Instant.EPOCH.plus(1, ChronoUnit.SECONDS);", + "}") + .doTest(); + } + + @Test + public void instantPlus_bad() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Instant;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I0 = Instant.EPOCH.plus(1, ChronoUnit.CENTURIES);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I1 = Instant.EPOCH.plus(1, ChronoUnit.DECADES);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I2 = Instant.EPOCH.plus(1, ChronoUnit.ERAS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I3 = Instant.EPOCH.plus(1, ChronoUnit.FOREVER);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I4 = Instant.EPOCH.plus(1, ChronoUnit.MILLENNIA);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I5 = Instant.EPOCH.plus(1, ChronoUnit.MONTHS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I6 = Instant.EPOCH.plus(1, ChronoUnit.WEEKS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I7 = Instant.EPOCH.plus(1, ChronoUnit.YEARS);", + "}") + .doTest(); + } + + @Test + public void instantMinus_good() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Instant;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " private static final Instant I0 = Instant.EPOCH.minus(1, ChronoUnit.DAYS);", + " private static final Instant I1 = Instant.EPOCH.minus(1, ChronoUnit.HALF_DAYS);", + " private static final Instant I2 = Instant.EPOCH.minus(1, ChronoUnit.HOURS);", + " private static final Instant I3 = Instant.EPOCH.minus(1, ChronoUnit.MICROS);", + " private static final Instant I4 = Instant.EPOCH.minus(1, ChronoUnit.MILLIS);", + " private static final Instant I5 = Instant.EPOCH.minus(1, ChronoUnit.MINUTES);", + " private static final Instant I6 = Instant.EPOCH.minus(1, ChronoUnit.NANOS);", + " private static final Instant I7 = Instant.EPOCH.minus(1, ChronoUnit.SECONDS);", + "}") + .doTest(); + } + + @Test + public void instantMinus_bad() { + helper + .addSourceLines( + "TestClass.java", + "import java.time.Instant;", + "import java.time.temporal.ChronoUnit;", + "public class TestClass {", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I0 = Instant.EPOCH.minus(1, ChronoUnit.CENTURIES);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I1 = Instant.EPOCH.minus(1, ChronoUnit.DECADES);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I2 = Instant.EPOCH.minus(1, ChronoUnit.ERAS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I3 = Instant.EPOCH.minus(1, ChronoUnit.FOREVER);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I4 = Instant.EPOCH.minus(1, ChronoUnit.MILLENNIA);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I5 = Instant.EPOCH.minus(1, ChronoUnit.MONTHS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I6 = Instant.EPOCH.minus(1, ChronoUnit.WEEKS);", + " // BUG: Diagnostic contains: InstantTemporalUnit", + " private static final Instant I7 = Instant.EPOCH.minus(1, ChronoUnit.YEARS);", + "}") + .doTest(); + } +}