[nullsafe] Primitive types are always Nonnull

Summary:
Primitive types are not annotated. Because of that, we used to implicitly derive
`DeclaredNonnull` type for them. This worked fine, but this leads to errors in Strict mode, which does
not believe DeclaredNonnull type.

Now lets offifically teach nullsafe that primitive types are
non-nullable.

Reviewed By: jvillard

Differential Revision: D18114623

fbshipit-source-id: 227217931
master
Mitya Lyubarskiy 5 years ago committed by Facebook Github Bot
parent 0d1d3dcd64
commit f9b0d06826

@ -28,7 +28,8 @@ let get tenv fn typ =
{ annotation_deprecated= annotation { annotation_deprecated= annotation
; annotated_type= ; annotated_type=
AnnotatedType. AnnotatedType.
{nullability= AnnotatedNullability.of_annot_item annotation ~is_strict_mode; typ} } { nullability= AnnotatedNullability.of_type_and_annotation typ annotation ~is_strict_mode
; typ } }
in in
Option.map Option.map
(Typ.Struct.get_field_type_and_annotation ~lookup fn typ) (Typ.Struct.get_field_type_and_annotation ~lookup fn typ)

@ -40,6 +40,7 @@ and declared_nonnull_origin =
and nonnull_origin = and nonnull_origin =
| ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *) | ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *)
| StrictMode (** under strict mode we consider non-null declarations to be trusted *) | StrictMode (** under strict mode we consider non-null declarations to be trusted *)
| PrimitiveType (** Primitive types are non-nullable by language design *)
[@@deriving compare] [@@deriving compare]
let get_nullability = function let get_nullability = function
@ -67,7 +68,13 @@ let pp fmt t =
match origin with AnnotatedNonnull -> "@" | ImplicitlyNonnull -> "implicit" match origin with AnnotatedNonnull -> "@" | ImplicitlyNonnull -> "implicit"
in in
let string_of_nonnull_origin nonnull_origin = let string_of_nonnull_origin nonnull_origin =
match nonnull_origin with ModelledNonnull -> "model" | StrictMode -> "strict" match nonnull_origin with
| ModelledNonnull ->
"model"
| StrictMode ->
"strict"
| PrimitiveType ->
"primitive"
in in
match t with match t with
| Nullable origin -> | Nullable origin ->
@ -78,14 +85,15 @@ let pp fmt t =
F.fprintf fmt "Nonnull[%s]" (string_of_nonnull_origin origin) F.fprintf fmt "Nonnull[%s]" (string_of_nonnull_origin origin)
let of_annot_item ~is_strict_mode ia = let of_type_and_annotation ~is_strict_mode typ annotations =
if Annotations.ia_is_nullable ia then if not (PatternMatch.type_is_class typ) then Nonnull PrimitiveType
else if Annotations.ia_is_nullable annotations then
let nullable_origin = let nullable_origin =
if Annotations.ia_is_propagates_nullable ia then AnnotatedPropagatesNullable if Annotations.ia_is_propagates_nullable annotations then AnnotatedPropagatesNullable
else AnnotatedNullable else AnnotatedNullable
in in
Nullable nullable_origin Nullable nullable_origin
else if is_strict_mode then Nonnull StrictMode else if is_strict_mode then Nonnull StrictMode
else if Annotations.ia_is_nonnull ia then DeclaredNonnull AnnotatedNonnull else if Annotations.ia_is_nonnull annotations then DeclaredNonnull AnnotatedNonnull
(* Currently, we treat not annotated types as nonnull *) (* Currently, we treat not annotated types as nonnull *)
else DeclaredNonnull ImplicitlyNonnull else DeclaredNonnull ImplicitlyNonnull

@ -43,12 +43,13 @@ and declared_nonnull_origin =
and nonnull_origin = and nonnull_origin =
| ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *) | ModelledNonnull (** nullsafe knows it is non-nullable via its internal models *)
| StrictMode (** under strict mode we consider non-null declarations to be trusted *) | StrictMode (** under strict mode we consider non-null declarations to be trusted *)
| PrimitiveType (** Primitive types are non-nullable by language design *)
[@@deriving compare] [@@deriving compare]
val get_nullability : t -> Nullability.t val get_nullability : t -> Nullability.t
val of_annot_item : is_strict_mode:bool -> Annot.Item.t -> t val of_type_and_annotation : is_strict_mode:bool -> Typ.t -> Annot.Item.t -> t
(** Converts the information from the annotation to nullability. (** Given the type and its annotations, returns its nullability.
NOTE: it does not take into account models etc., so this is intended to be used NOTE: it does not take into account models etc., so this is intended to be used
as a helper function for more high-level annotation processing. as a helper function for more high-level annotation processing.
*) *)

@ -28,8 +28,11 @@ and param_signature =
[@@deriving compare] [@@deriving compare]
(* get nullability of method's return type given its annotations and information about its params *) (* get nullability of method's return type given its annotations and information about its params *)
let nullability_for_return ia ~is_strict_mode ~has_propagates_nullable_in_param = let nullability_for_return ret_type ret_annotations ~is_strict_mode
let nullability = AnnotatedNullability.of_annot_item ~is_strict_mode ia in ~has_propagates_nullable_in_param =
let nullability =
AnnotatedNullability.of_type_and_annotation ~is_strict_mode ret_type ret_annotations
in
(* if any param is annotated with propagates nullable, then the result is nullable *) (* if any param is annotated with propagates nullable, then the result is nullable *)
match nullability with match nullability with
| AnnotatedNullability.Nullable _ -> | AnnotatedNullability.Nullable _ ->
@ -43,9 +46,10 @@ let nullability_for_return ia ~is_strict_mode ~has_propagates_nullable_in_param
(* Given annotations for method signature, extract nullability information (* Given annotations for method signature, extract nullability information
for return type and params *) for return type and params *)
let extract_nullability ~is_strict_mode return_annotation param_annotations = let extract_nullability ~is_strict_mode ret_type ret_annotations param_annotated_types =
let params_nullability = let params_nullability =
List.map param_annotations ~f:(AnnotatedNullability.of_annot_item ~is_strict_mode) List.map param_annotated_types ~f:(fun (typ, annotations) ->
AnnotatedNullability.of_type_and_annotation typ annotations ~is_strict_mode )
in in
let has_propagates_nullable_in_param = let has_propagates_nullable_in_param =
List.exists params_nullability ~f:(function List.exists params_nullability ~f:(function
@ -55,13 +59,14 @@ let extract_nullability ~is_strict_mode return_annotation param_annotations =
false ) false )
in in
let return_nullability = let return_nullability =
nullability_for_return return_annotation ~is_strict_mode ~has_propagates_nullable_in_param nullability_for_return ret_type ret_annotations ~is_strict_mode
~has_propagates_nullable_in_param
in in
(return_nullability, params_nullability) (return_nullability, params_nullability)
let get ~is_strict_mode proc_attributes : t = let get ~is_strict_mode proc_attributes : t =
let Annot.Method.{return= return_annotation; params= original_params_annotation} = let Annot.Method.{return= ret_annotation; params= original_params_annotation} =
proc_attributes.ProcAttributes.method_annotation proc_attributes.ProcAttributes.method_annotation
in in
let formals = proc_attributes.ProcAttributes.formals in let formals = proc_attributes.ProcAttributes.formals in
@ -85,12 +90,14 @@ let get ~is_strict_mode proc_attributes : t =
in in
List.rev (zip_params (List.rev original_params_annotation) (List.rev formals)) List.rev (zip_params (List.rev original_params_annotation) (List.rev formals))
in in
let _, final_params_annotation = List.unzip params_with_annotations in let param_annotated_types =
List.map params_with_annotations ~f:(fun ((_, typ), annotations) -> (typ, annotations))
in
let return_nullability, params_nullability = let return_nullability, params_nullability =
extract_nullability ~is_strict_mode return_annotation final_params_annotation extract_nullability ~is_strict_mode ret_type ret_annotation param_annotated_types
in in
let ret = let ret =
{ ret_annotation_deprecated= return_annotation { ret_annotation_deprecated= ret_annotation
; ret_annotated_type= AnnotatedType.{nullability= return_nullability; typ= ret_type} } ; ret_annotated_type= AnnotatedType.{nullability= return_nullability; typ= ret_type} }
in in
let params = let params =

@ -135,6 +135,12 @@ class Strict {
return (new NonStrict()).getNullable(); 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();
}
private String nonStrictClass_convertingNonnullToNonnullIsBad() { private String nonStrictClass_convertingNonnullToNonnullIsBad() {
// even that it is declared as nonnull, can not convert it to nonnull it without checking before // even that it is declared as nonnull, can not convert it to nonnull it without checking before
return (new NonStrict()).getNonnull(); return (new NonStrict()).getNonnull();
@ -235,4 +241,8 @@ class NonStrict {
public static String staticNonnull() { public static String staticNonnull() {
return ""; return "";
} }
public static int getPrimitiveTypeValue() {
return 0;
}
} }

@ -216,7 +216,7 @@ codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.n
codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.return_null_in_catch():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `return_null_in_catch()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 160)] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.return_null_in_catch():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `return_null_in_catch()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 160)]
codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.return_null_in_catch_after_throw():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `return_null_in_catch_after_throw()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 172)] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.return_null_in_catch_after_throw():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `return_null_in_catch_after_throw()` may return null but it is not annotated with `@Nullable`. (Origin: null constant at line 172)]
codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.tryWithResourcesReturnNullable(java.lang.String):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `tryWithResourcesReturnNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to nullToNullableIsOK() at line 142)] codetoanalyze/java/nullsafe-default/ReturnNotNullable.java, codetoanalyze.java.nullsafe_default.ReturnNotNullable.tryWithResourcesReturnNullable(java.lang.String):java.lang.Object, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `tryWithResourcesReturnNullable(...)` may return null but it is not annotated with `@Nullable`. (Origin: call to nullToNullableIsOK() at line 142)]
codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNonnullToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `nonStrictClass_convertingNonnullToNonnullIsBad()` may return null but it is not annotated with `@Nullable`. (Origin: call to getNonnull() at line 140)] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNonnullToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `nonStrictClass_convertingNonnullToNonnullIsBad()` may return null but it is not annotated with `@Nullable`. (Origin: call to getNonnull() at line 146)]
codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNonnullToNullableIsOK():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `nonStrictClass_convertingNonnullToNullableIsOK()` is annotated with `@Nullable` but never returns null.] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNonnullToNullableIsOK():java.lang.String, 0, ERADICATE_RETURN_OVER_ANNOTATED, no_bucket, WARNING, [Method `nonStrictClass_convertingNonnullToNullableIsOK()` is annotated with `@Nullable` but never returns null.]
codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNullableToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `nonStrictClass_convertingNullableToNonnullIsBad()` may return null but it is not annotated with `@Nullable`. (Origin: call to getNullable() at line 135)] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_convertingNullableToNonnullIsBad():java.lang.String, 0, ERADICATE_RETURN_NOT_NULLABLE, no_bucket, WARNING, [Method `nonStrictClass_convertingNullableToNonnullIsBad()` may return null but it is not annotated with `@Nullable`. (Origin: call to getNullable() at line 135)]
codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_dereferenceNonnullFieldAfterCheckIsOK():void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, WARNING, [The condition NonStrict.nonnull is always true according to the existing annotations.] codetoanalyze/java/nullsafe-default/StrictMode.java, codetoanalyze.java.nullsafe_default.Strict.nonStrictClass_dereferenceNonnullFieldAfterCheckIsOK():void, 2, ERADICATE_CONDITION_REDUNDANT, no_bucket, WARNING, [The condition NonStrict.nonnull is always true according to the existing annotations.]

Loading…
Cancel
Save