You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
7.6 KiB

/*
* 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;
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
}
}
}
// nullsafe should assume Object.equals() and all overrides return false on `null` argument.
static class TestEqualsIsFalseOnNull {
// An example of class that overrides equals().
static class SomeObject {
private int mContent;
SomeObject(int content) {
mContent = content;
}
@Override
// No nullsafe warnings are expected
public boolean equals(@Nullable Object src) {
if (!super.equals(src)) {
return false;
}
// at this point src should be a non-nullable: super.equals() would return false otherwise.
src.toString(); // should be OK to dereference now
if (!(src instanceof SomeObject)) {
return false;
}
SomeObject asSomeObject = (SomeObject) src;
return mContent == asSomeObject.mContent;
}
}
private void testObjectEqualsIsFalseOnNull(Object x, @Nullable Object y) {
if (!x.equals(y)) {
return;
}
// OK to dereference
y.toString();
}
private void testOverrideEqualsIsFalseOnNull(SomeObject x, @Nullable Object y) {
if (!x.equals(y)) {
return;
}
// OK to dereference even that SomeObject.equals() is not annotated as @FalseOnNull
y.toString();
}
}
// 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
}
}
}