|
|
|
/*
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|