From 07887ad30fb23dea865f2def75c3700808a5a8a7 Mon Sep 17 00:00:00 2001 From: Artem Pianykh Date: Thu, 30 Jul 2020 08:52:48 -0700 Subject: [PATCH] [nullsafe] Add FP tests for typecasts and generics use Reviewed By: jvillard Differential Revision: D22841625 fbshipit-source-id: a0bbb1d61 --- .../java/nullsafe/ReturnNotNullable.java | 41 +++++++++++++++++++ .../codetoanalyze/java/nullsafe/issues.exp | 19 +++++---- .../nullsafe/third-party-signatures/java.sig | 1 + 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java b/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java index 0e91d5fdd..4a0e094d3 100644 --- a/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java +++ b/infer/tests/codetoanalyze/java/nullsafe/ReturnNotNullable.java @@ -12,6 +12,10 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -224,5 +228,42 @@ public class ReturnNotNullable { return error; } + + // This case is different from the one above in 2 ways: + // 1. The argument is a generic, + // 2. The type parameter is not {@code Object}. + // Both are important to trigger the behaviour we're checking (indirection via typecast in CFG). + public List nullCheckGenericAssignmentResultAsNonnullOk_FP( + BlockingQueue queue) { + final ArrayList records = new ArrayList<>(queue.size()); + try { + Runnable task; + // null-check should refine nullability of task + while ((task = queue.poll(0, TimeUnit.MILLISECONDS)) != null) { + records.add(task.toString()); + } + } catch (InterruptedException ie) { + // Ignore exception + } + + return records; + } + + static class NullableGetter { + @Nullable public T mInner; + + @Nullable + public T get() { + return mInner; + } + } + + public void chainedCallsWithAssignmentChecksOk_FP(@Nullable NullableGetter c1) { + NullableGetter c2, c3; + + if (c1 != null && (c2 = c1.get()) != null && (c3 = c2.get()) != null) { + c3.get(); + } + } } } diff --git a/infer/tests/codetoanalyze/java/nullsafe/issues.exp b/infer/tests/codetoanalyze/java/nullsafe/issues.exp index d01a46ead..4dcaebcc9 100644 --- a/infer/tests/codetoanalyze/java/nullsafe/issues.exp +++ b/infer/tests/codetoanalyze/java/nullsafe/issues.exp @@ -301,19 +301,22 @@ codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.] codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 11, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.] codetoanalyze/java/nullsafe/PropagatesNullable.java, codetoanalyze.java.nullsafe.TestPropagatesNullable$TestSecondParameter.test(java.lang.String,java.lang.String):void, 15, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`nullable(...)` is nullable and is not locally checked for null when calling `length()`.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, Linters_dummy_method, 19, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [], ReturnNotNullable, codetoanalyze.java.nullsafe, issues: 9, curr_mode: "Default" -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$ConditionalAssignment.test(boolean):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`test(...)`: return type is declared non-nullable but the method returns a nullable value: field f1 at line 199.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, Linters_dummy_method, 23, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [], ReturnNotNullable, codetoanalyze.java.nullsafe, issues: 12, curr_mode: "Default" +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$AssignmentResultCheck.chainedCallsWithAssignmentChecksOk_FP(codetoanalyze.java.nullsafe.ReturnNotNullable$AssignmentResultCheck$NullableGetter):void, 3, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`c2` is nullable and is not locally checked for null when calling `get()`: call to get() at line 264.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$AssignmentResultCheck.chainedCallsWithAssignmentChecksOk_FP(codetoanalyze.java.nullsafe.ReturnNotNullable$AssignmentResultCheck$NullableGetter):void, 4, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`c3` is nullable and is not locally checked for null when calling `get()`: call to get() at line 264.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$AssignmentResultCheck.nullCheckGenericAssignmentResultAsNonnullOk_FP(java.util.concurrent.BlockingQueue):java.util.List, 7, ERADICATE_NULLABLE_DEREFERENCE, no_bucket, WARNING, [`task` is nullable and is not locally checked for null when calling `toString()`: call to BlockingQueue.poll(...) at line 242 (declared nullable in nullsafe/third-party-signatures/java.sig at line 3).] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable$ConditionalAssignment.test(boolean):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`test(...)`: return type is declared non-nullable but the method returns a nullable value: field f1 at line 203.] codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.constantToNullableIsOverannotated():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `constantToNullableIsOverannotated()` is annotated with `@Nullable` but never returns null.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.getResourceNullable(java.lang.Class,java.lang.String):java.net.URL, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`getResourceNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to Class.getResource(...) at line 177 (nullable according to nullsafe internal models).] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.getResourceNullable(java.lang.Class,java.lang.String):java.net.URL, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`getResourceNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to Class.getResource(...) at line 181 (nullable according to nullsafe internal models).] codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nonNullToNullableIsOverannotated(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `nonNullToNullableIsOverannotated(...)` is annotated with `@Nullable` but never returns null.] codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.notAnnotatedNullableIsOverannotated(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, ADVICE, [Method `notAnnotatedNullableIsOverannotated(...)` is annotated with `@Nullable` but never returns null.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNonnullIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 60.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNotAnnotatedIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNotAnnotatedIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 35.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNonnullIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 64.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullToNotAnnotatedIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullToNotAnnotatedIsBad()`: return type is declared non-nullable but the method returns `null`: null constant at line 39.] codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullableToNonnullIsBad(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullableToNonnullIsBad(...)`: return type is declared non-nullable but the method returns a nullable value: method parameter s.] codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.nullableToNotAnnotatedIsBad(java.lang.String):java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`nullableToNotAnnotatedIsBad(...)`: return type is declared non-nullable but the method returns a nullable value: method parameter s.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch()`: return type is declared non-nullable but the method returns `null`: null constant at line 160.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch_after_throw():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch_after_throw()`: return type is declared non-nullable but the method returns `null`: null constant at line 172.] -codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.tryWithResourcesReturnNullable(java.lang.String):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`tryWithResourcesReturnNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to nullToNullableIsOK() at line 142.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch()`: return type is declared non-nullable but the method returns `null`: null constant at line 164.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.return_null_in_catch_after_throw():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`return_null_in_catch_after_throw()`: return type is declared non-nullable but the method returns `null`: null constant at line 176.] +codetoanalyze/java/nullsafe/ReturnNotNullable.java, codetoanalyze.java.nullsafe.ReturnNotNullable.tryWithResourcesReturnNullable(java.lang.String):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [`tryWithResourcesReturnNullable(...)`: return type is declared non-nullable but the method returns a nullable value: call to nullToNullableIsOK() at line 146.] codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 15, ERADICATE_META_CLASS_NEEDS_IMPROVEMENT, no_bucket, INFO, [], Strict, codetoanalyze.java.nullsafe, issues: 17, curr_mode: "Strict" codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 239, ERADICATE_META_CLASS_IS_NULLSAFE, no_bucket, INFO, [], OtherStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Strict" codetoanalyze/java/nullsafe/StrictMode.java, Linters_dummy_method, 260, ERADICATE_META_CLASS_CAN_BE_NULLSAFE, no_bucket, ADVICE, [Congrats! `NonStrict` is free of nullability issues. Mark it `@Nullsafe(Nullsafe.Mode.LOCAL)` to prevent regressions.], NonStrict, codetoanalyze.java.nullsafe, issues: 0, curr_mode: "Default", promote_mode: "LocalTrustAll" diff --git a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig b/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig index f55cdc9af..251b0d660 100644 --- a/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig +++ b/infer/tests/codetoanalyze/java/nullsafe/third-party-signatures/java.sig @@ -1,2 +1,3 @@ java.lang.String#concat(java.lang.String) java.lang.Throwable#getCause() @Nullable +java.util.concurrent.BlockingQueue#poll(long, java.util.concurrent.TimeUnit) @Nullable