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.

296 lines
8.8 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 com.facebook.infer.annotation.NullsafeStrict;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@NullsafeStrict
class Strict {
public String notInitializedIsBAD;
public @Nullable String nullable;
public @Nonnull String nonnull = "";
public @Nullable String getNullable() {
return nullable;
}
public String getNonnull() {
return nonnull;
}
public static @Nullable String staticNullable() {
return null;
}
public static String staticNonnull() {
return "";
}
// 1. Inside the same class, we trust annotations.
private void sameClass_dereferenceNullableMethodIsBad() {
getNullable().toString();
}
private void sameClass_dereferenceNonnullMethodIsOK() {
getNonnull().toString();
}
private void sameClass_dereferenceNullableStaticMethodIsBad() {
staticNullable().toString();
}
private void sameClass_dereferenceNonnullStaticMethodIsOK() {
staticNonnull().toString();
}
private void sameClass_dereferenceNullableFieldIsBad() {
nullable.toString();
}
private void sameClass_dereferenceNonnullFieldIsOK() {
nonnull.toString();
}
private String sameClass_convertingNullableToNonnullIsBad() {
return getNullable();
}
private String sameClass_convertingNonnullToNonnullIsOK() {
return getNonnull();
}
// 2. We trust annotations that came from other classes annotated as strict
private void strictClass_dereferenceNullableMethodIsBad() {
(new OtherStrict()).getNullable().toString();
}
private void strictClass_dereferenceNonnullMethodIsOK() {
(new OtherStrict()).getNonnull().toString();
}
private void strictClass_dereferenceNullableStaticMethodIsBad() {
OtherStrict.staticNullable().toString();
}
private void strictClass_dereferenceNonnullStaticMethodIsOK() {
OtherStrict.staticNonnull().toString();
}
private void strictClass_dereferenceNullableFieldIsBad() {
(new OtherStrict()).nullable.toString();
}
private void strictClass_dereferenceNonnullFieldIsOK() {
(new OtherStrict()).nonnull.toString();
}
private String strictClass_convertingNullableToNonnullIsBad() {
return (new OtherStrict()).getNullable();
}
private String strictClass_convertingNonnullToNonnullIsOK() {
return (new OtherStrict()).getNonnull();
}
// 3. We DON'T trust annotations that came from other classes NOT annotated as strict
// when it comes to dereferencing or converting them to nullable.
private void nonStrictClass_dereferenceNullableMethodIsBad() {
(new NonStrict()).getNullable().toString();
}
private void nonStrictClass_dereferenceNonnullMethodIsBad() {
// even that it is declared as nonnull, can not dereference it without checking before
(new NonStrict()).getNonnull().toString();
}
private void nonStrictClass_dereferenceNullableStaticMethodIsBad() {
NonStrict.staticNullable().toString();
}
private void nonStrictClass_dereferenceNonnullStaticMethodIsBad() {
// even that it is declared as nonnull, can not dereference it without checking before
NonStrict.staticNonnull().toString();
}
private void nonStrictClass_dereferenceNullableFieldIsBad() {
(new NonStrict()).nullable.toString();
}
private void nonStrictClass_dereferenceNonnullFieldIsBad() {
// even that it is declared as nonnull, can not dereference it without checking before
(new NonStrict()).nonnull.toString();
}
private String nonStrictClass_convertingNullableToNonnullIsBad() {
return (new NonStrict()).getNullable();
}
public int usingPrimitiveTypesFromNonStrictIsOK() {
// Of course, primitive types can not be nullable so it
// does not matter if they are used from NonStrict
return NonStrict.getPrimitiveTypeValue();
}
// We don't require strictifying enums: their values are non-nullables
private SomeEnum usingEnumValuesAsNonnullIsOK() {
String str = SomeEnum.FIRST_VALUE.toString(); // can dereference
SomeEnum.FIRST_VALUE.someMethod(); // can dereference via calling a enum-specific method
return SomeEnum.SECOND_VALUE; // can convert to nonnull
}
// FAKE_VALUE is not a value, but just a static fields so should require strictification
// (but it does not)
private SomeEnum FN_usingNonStrictifiedStaticFieldsInEnumsShouldBeBad() {
String str = SomeEnum.FAKE_VALUE.toString(); // should not be able to dereference
SomeEnum.FAKE_VALUE.someMethod(); // should not be able to dereference
return SomeEnum.FAKE_VALUE; // should not be able to convert to nonnull
}
private SomeEnum[] enumValuesIsNonnullable() {
// values() is a special enum method which is never nullable
return SomeEnum.values();
}
private SomeEnum enumValueOfIsNonnullable() {
// valueOf() is a special enum method which is never nullable
return SomeEnum.valueOf("this will throw but won't return null");
}
private Boolean booleanFALSE_NonNullableOK() {
return Boolean.FALSE;
}
private Boolean booleanTRUE_NonNullableOK() {
return Boolean.TRUE;
}
private String nonStrictClass_convertingNonnullToNonnullIsBad() {
// even that it is declared as nonnull, can not convert it to nonnull it without checking before
return (new NonStrict()).getNonnull();
}
// 4. We don't completely prohibit using non-strict from strict, but we do require extra checks
// or adding defensive annotations.
private void nonStrictClass_dereferenceNonnullFieldAfterCheckIsOK() {
NonStrict o = new NonStrict();
if (o.nonnull != null) {
// This works because Nullsafe assumes that the field won't be modified between the calls
// (e.g. in multithreading context)
o.nonnull.toString();
}
}
private void nonStrictClass_dereferenceNonnullMethodAfterCheckIsOK() {
NonStrict o = new NonStrict();
if (o.getNonnull() != null) {
// This works because Nullsafe assumes that all methods are determenistic and side-effect
// free, so the second call won't return null.
o.getNonnull().toString();
}
}
private @Nullable String nonStrictClass_convertingNonnullToNullableIsOK() {
return (new NonStrict()).getNonnull();
}
// 5. Main promise of strict mode: if the function is not annotated as nullable,
// it won't return null.
// So, if a function is annotated as strict, no extra check on caller's side is required.
// But strict mode DOES NOT guarantee there will be absolutely no NPE in callees:
// a) Even if strict mode calls only strict mode, there can be assertions that will throw NPE.
// b) We allow calling non-strict functions, and they internally can be inconsistent and throw
// NPE.
// c) We even allow passing values obtained from non-strict code, to other parts of non-strict
// code (e.g. we allow glueing 2 non-strict classes together inside a strict class, which might
// potentially lead to NPE if one of annotations is untrusted).
private void propagatingNonnullFromNonStrictToStrictIsBad() {
NonStrict nonStrict = new NonStrict();
OtherStrict strict = new OtherStrict();
nonStrict.nonnull = strict.getNonnull();
}
private void propagatingNonnullBetweenTwoNonStrictObjectsIsOK() {
NonStrict nonStrict1 = new NonStrict();
NonStrict nonStrict2 = new NonStrict();
// Though getNonnull() is declared as non-nullable, is not strictly checked
// so there is a possibility that it can return null, e.g. if it calls to a third-party
// libraries. this null can leak to `nonnull` field, which might be a bad thing and lead to
// issues.
// This is OK for strict mode.
nonStrict1.nonnull = nonStrict2.getNonnull();
}
}
@NullsafeStrict
class OtherStrict {
public @Nullable String nullable;
public String nonnull = "";
public @Nullable String getNullable() {
return nullable;
}
public String getNonnull() {
return nonnull;
}
public static @Nullable String staticNullable() {
return null;
}
public static String staticNonnull() {
return "";
}
}
class NonStrict {
public @Nullable String nullable;
public String nonnull = "";
public @Nullable String getNullable() {
return nullable;
}
public String getNonnull() {
return nonnull;
}
public static @Nullable String staticNullable() {
return null;
}
public static String staticNonnull() {
return "";
}
public static int getPrimitiveTypeValue() {
return 0;
}
}
enum SomeEnum {
FIRST_VALUE,
SECOND_VALUE;
public void someMethod() {}
// We currently have no way to distinct that guy from enum value.
// Using it will lead to NPE, but we won't detect it
// This should not occur in practice often, hopefully :)
public static SomeEnum FAKE_VALUE;
}