/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package codetoanalyze.java.nullsafe_default; import android.text.TextUtils; import com.facebook.infer.annotation.FalseOnNull; import com.facebook.infer.annotation.TrueOnNull; import com.google.common.base.Strings; import javax.annotation.Nullable; /** Testing functionality related to @TrueOnNull and @FalseOnNull methods */ public class TrueFalseOnNull { static class StaticOneParam { @TrueOnNull static boolean trueOnNull(@Nullable Object o) { return o == null ? true : o.toString() == "something"; } @FalseOnNull static boolean falseOnNull(@Nullable Object o) { return o == null ? false : o.toString() == "something"; } static boolean notAnnotated(@Nullable Object o) { return o == null ? false : o.toString() == "something"; } } static class NonStaticOneParam { private String compareTo = "something"; @TrueOnNull boolean trueOnNull(@Nullable Object o) { return o == null ? true : o.toString() == compareTo; } @FalseOnNull boolean falseOnNull(@Nullable Object o) { return o == null ? false : o.toString() == compareTo; } boolean notAnnotated(@Nullable Object o) { return o == null ? false : o.toString() == compareTo; } } // @TrueOnNull and @FalseOnNull should expect true/false will be returned if ANY of input objects // is null. // In other words, they should infer that all input nullable objects are non-null in the // corresponding branch. static class NonStaticSeveralParams { private String compareTo = "something"; @TrueOnNull boolean trueOnNull( @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) { if (nullable1 == null || nullable2 == null) { return true; } return nonnull == compareTo; } @FalseOnNull boolean falseOnNull( @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) { if (nullable1 == null || nullable2 == null) { return false; } return nonnull == compareTo; } boolean notAnnotated( @Nullable Object nullable1, int primitive, Object nonnull, @Nullable Object nullable2) { if (nullable1 == null || nullable2 == null) { return false; } return nonnull == compareTo; } } class TestStaticOneParam { void trueOnNullPositiveBranchIsBAD(@Nullable String s) { if (StaticOneParam.trueOnNull(s)) { s.toString(); } } void trueOnNullNegativeBranchIsOK(@Nullable String s) { if (!StaticOneParam.trueOnNull(s)) { s.toString(); } } void falseOnNullPositiveBranchIsOK(@Nullable String s) { if (StaticOneParam.falseOnNull(s)) { s.toString(); } } void falseOnNullNegativeBranchIsBAD(@Nullable String s) { if (!StaticOneParam.falseOnNull(s)) { s.toString(); } } void notAnnotatedPositiveBranchIsBAD(@Nullable String s) { if (StaticOneParam.notAnnotated(s)) { s.toString(); } } void notAnnotatedNegativeBranchIsBAD(@Nullable String s) { if (!StaticOneParam.notAnnotated(s)) { s.toString(); } } } class TestNonStaticOneParam { private NonStaticOneParam object = new NonStaticOneParam(); void trueOnNullPositiveBranchIsBAD(@Nullable String s) { if (object.trueOnNull(s)) { s.toString(); } } void trueOnNullNegativeBranchIsOK(@Nullable String s) { if (!object.trueOnNull(s)) { s.toString(); } } void falseOnNullPositiveBranchIsOK(@Nullable String s) { if (object.falseOnNull(s)) { s.toString(); } } void falseOnNullNegativeBranchIsBAD(@Nullable String s) { if (!object.falseOnNull(s)) { s.toString(); } } void notAnnotatedPositiveBranchIsBAD(@Nullable String s) { if (object.notAnnotated(s)) { s.toString(); } } void notAnnotatedNegativeBranchIsBAD(@Nullable String s) { if (!object.notAnnotated(s)) { s.toString(); } } } class TestNonStaticSeveralParams { private NonStaticSeveralParams object = new NonStaticSeveralParams(); void trueOnNullPositiveBranchIsBAD(@Nullable String s1, @Nullable String s2) { if (object.trueOnNull(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } void trueOnNullNegativeBranchIsOK(@Nullable String s1, @Nullable String s2) { if (!object.trueOnNull(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } void falseOnNullPositiveBranchIsOK(@Nullable String s1, @Nullable String s2) { if (object.falseOnNull(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } void falseOnNullNegativeBranchIsBAD(@Nullable String s1, @Nullable String s2) { if (!object.falseOnNull(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } void notAnnotatedPositiveBranchIsBAD(@Nullable String s1, @Nullable String s2) { if (object.notAnnotated(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } void notAnnotatedNegativeBranchIsBAD(@Nullable String s1, @Nullable String s2) { if (!object.notAnnotated(s1, 1, "123", s2)) { s1.toString(); s2.toString(); } } } class TestModels { void testModelledTrueOnNull(@Nullable String s) { // TextUtils.isEmpty is modelled as TrueOnNull if (!TextUtils.isEmpty(s)) { s.toString(); // OK: if we are here, we know that `s` is not null } // Strings.isNullOrEmpty is modelled as TrueOnNull if (!Strings.isNullOrEmpty(s)) { s.toString(); // OK: if we are here, we know that `s` is not null } } } // this is a common enough pattern to be tested separately class EarlyReturn { void testEarlyReturn(@Nullable CharSequence s1, @Nullable CharSequence s2) { if (StaticOneParam.trueOnNull(s1) || StaticOneParam.notAnnotated(s2)) { return; } s1.toString(); // OK: if `s1` was null, we would no rech this point s2.toString(); // BAD: no reason for `s2` to become non-nullable } } }