/* * Copyright (c) 2013-present, Facebook, Inc. * * 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.eradicate; import android.text.TextUtils; import com.facebook.infer.annotation.FalseOnNull; import com.facebook.infer.annotation.PropagatesNullable; import com.facebook.infer.annotation.TrueOnNull; import com.google.common.base.Strings; import javax.annotation.Nullable; public class CustomAnnotations { static class MyTextUtils { @TrueOnNull static boolean isEmpty(@Nullable java.lang.CharSequence s) { return s == null || s.equals(""); } @FalseOnNull static boolean isNotEmpty(@Nullable java.lang.CharSequence s) { return s != null && s.length() > 0; } } class TestTextUtilsIsEmpty { void textUtilsNotIsEmpty(@Nullable CharSequence s) { if (!TextUtils.isEmpty(s)) { s.toString(); // OK } } void textUtilsIsEmpty(@Nullable CharSequence s) { if (TextUtils.isEmpty(s)) { s.toString(); // BAD } } void myTextUtilsNotIsEmpty(@Nullable CharSequence s) { if (!MyTextUtils.isEmpty(s)) { s.toString(); // OK } } void myTextUtilsIsEmpty(@Nullable CharSequence s) { if (MyTextUtils.isEmpty(s)) { s.toString(); // BAD } } void myTextUtilsIsNotEmpty(@Nullable CharSequence s) { if (MyTextUtils.isNotEmpty(s)) { s.toString(); // OK } } void myTextUtilsNotIsNotEmpty(@Nullable CharSequence s) { if (!MyTextUtils.isNotEmpty(s)) { s.toString(); // BAD } } } // Tests for the annotation @PropagatesNullable class TestPropagatesNullable { // one parameter: means return null iff s is null String m(@PropagatesNullable String s) { return s; } void mBad() { m(null).length(); // bad: m returns null } void mGood() { m("").length(); } // limitation: we currently cannot check the body, and just trust the annotation String cannotCheckBody(@PropagatesNullable String s) { return null; // nothing is reported here } void illustrateFalseNegativeAsCannotCheckBody() { cannotCheckBody("").length(); // this is an NPE but is not found } // second parameter: means return null iff s2 is null String m2(@Nullable String s1, @PropagatesNullable String s2) { return s2; } void m2Bad() { m2(null, null).length(); // bad: m2 returns null } void m2Good() { m2(null, "").length(); } // two parameters: means return null iff either s1 or s2 is null String m12(@PropagatesNullable String s1, @PropagatesNullable String s2) { return s1 == null ? s1 : s2; } void m12Bad1() { m12(null, "").length(); // bad: m12 returns null } void m12Bad2() { m12("", null).length(); // bad: m12 returns null } void m12Good() { m12("", "").length(); } } class TestModeledTrueOnNull { void testIsEmptyOrNullOk(@Nullable String string) { if (!Strings.isNullOrEmpty(string)) { string.contains("Infer"); } } void testIsEmptyOrNullBad(@Nullable String string) { if (Strings.isNullOrEmpty(string)) { string.contains("Infer"); } } } }