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.
103 lines
3.0 KiB
103 lines
3.0 KiB
5 years ago
|
/*
|
||
|
* 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 {
|
||
|
|
||
|
// Example of API that benefits from annotating with @TrueOnNull and @FalseOnNull
|
||
|
static class AnnotatedTextUtils {
|
||
|
|
||
|
@TrueOnNull
|
||
|
static boolean isEmpty(@Nullable CharSequence s) {
|
||
|
return s == null || s.equals("");
|
||
|
}
|
||
|
|
||
|
@FalseOnNull
|
||
|
static boolean isNotEmpty(@Nullable CharSequence s) {
|
||
|
return s != null && s.length() > 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The same API, but not annotated
|
||
|
static class NotAnnotatedTextUtils {
|
||
|
static boolean isEmpty(@Nullable CharSequence s) {
|
||
|
return s == null || s.equals("");
|
||
|
}
|
||
|
|
||
|
static boolean isNotEmpty(@Nullable CharSequence s) {
|
||
|
return s != null && s.length() > 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Test {
|
||
|
void testTrueOnNull(@Nullable CharSequence s) {
|
||
|
// Explicitly annotated
|
||
|
if (!AnnotatedTextUtils.isEmpty(s)) {
|
||
|
s.toString(); // OK: if we are here, we know that s is not null
|
||
|
}
|
||
|
|
||
|
// Not annotated
|
||
|
if (!NotAnnotatedTextUtils.isEmpty(s)) {
|
||
|
s.toString(); // BAD: the typecker does not know s can not be null
|
||
|
}
|
||
|
|
||
|
if (AnnotatedTextUtils.isEmpty(s)) {
|
||
|
s.toString(); // BAD: s can be null or an empty string
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testFalseOnNull(@Nullable CharSequence s) {
|
||
|
// Explicitly annotated
|
||
|
if (AnnotatedTextUtils.isNotEmpty(s)) {
|
||
|
s.toString(); // OK: if we are here, we know that `s` is not null
|
||
|
}
|
||
|
|
||
|
// Not annotated
|
||
|
if (NotAnnotatedTextUtils.isNotEmpty(s)) {
|
||
|
s.toString(); // BAD: the typecker does not know `s` can not be null
|
||
|
}
|
||
|
|
||
|
if (!AnnotatedTextUtils.isNotEmpty(s)) {
|
||
|
s.toString(); // BAD: `s` can be null or an empty string
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testModelledTrueOnNull(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
|
||
|
}
|
||
|
|
||
|
if (!NotAnnotatedTextUtils.isEmpty(s)) {
|
||
|
s.toString(); // BAD: the typechecker can not figure this out for not modelled class
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void testEarlyReturn(@Nullable CharSequence s1, @Nullable CharSequence s2) {
|
||
|
if (AnnotatedTextUtils.isEmpty(s1) || NotAnnotatedTextUtils.isEmpty(s2)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
s1.toString(); // OK: if `s1` was null, we would no rech this point
|
||
|
s2.toString(); // BAD: typechecker can not figure this for not annotated class
|
||
|
}
|
||
|
}
|
||
|
}
|