[nullsafe] Make ErrorRenderingUtils `None`-safe

Summary:
# Problem

Yes, nullsafe is not null-safe, such an irony.

ErrorRenderingUtils overuses `option` and `let+` constructions. Most of
internal functions can return `None` when "something is wrong".

On top of this, "default" pattern match is overused either.

Because of this, `ErrorRenderingUtils.mk_nullsafe_special_issue` returns
optional type. In practice, this result can be None for many unclear
reasons, and it is super tricky to even understand them all.

This in turn forced AssignmentRule and DereferenceRule to process this
None is defensive way. The rules have some theory why None was returned,
and have assertions along the way.

Turns out those theories might be wrong. This diff will make triaging wrong assumptions easier.

Reviewed By: artempyanykh

Differential Revision: D20535720

fbshipit-source-id: 2b81e25b7
master
Mitya Lyubarskiy 5 years ago committed by Facebook GitHub Bot
parent ce1ba6a8c6
commit e826736a06

@ -63,7 +63,7 @@ module ReportableViolation = struct
let mk_description_for_bad_param_passed let mk_description_for_bad_param_passed
{model_source; param_signature; actual_param_expression; param_position; function_procname} {model_source; param_signature; actual_param_expression; param_position; function_procname}
~param_nullability ~nullability_evidence = ~param_nullability_kind ~nullability_evidence =
let nullability_evidence_as_suffix = let nullability_evidence_as_suffix =
Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:""
in in
@ -73,15 +73,11 @@ module ReportableViolation = struct
if String.equal actual_param_expression "null" then "is `null`" if String.equal actual_param_expression "null" then "is `null`"
else else
let nullability_descr = let nullability_descr =
match param_nullability with match param_nullability_kind with
| Nullability.Null -> | ErrorRenderingUtils.UserFriendlyNullable.Null ->
"`null`" "`null`"
| Nullability.Nullable -> | ErrorRenderingUtils.UserFriendlyNullable.Nullable ->
"nullable" "nullable"
| other ->
Logging.die InternalError
"mk_description_for_bad_param:: invariant violation: unexpected nullability %a"
Nullability.pp other
in in
Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr
in in
@ -147,82 +143,81 @@ module ReportableViolation = struct
IssueType.eradicate_return_not_nullable IssueType.eradicate_return_not_nullable
let mk_nullsafe_issue_for_explicitly_nullable_values ~assignment_type ~rhs_origin
~explicit_rhs_nullable_kind ~assignment_location =
let nullability_evidence =
get_origin_opt assignment_type rhs_origin
|> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin)
in
let nullability_evidence_as_suffix =
Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:""
in
let module MF = MarkupFormatter in
let alternative_method_description =
ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin
in
let alternative_recommendation =
Option.value_map alternative_method_description
~f:(fun descr ->
Format.asprintf " If you don't expect null, use %a instead." MF.pp_monospaced descr )
~default:""
in
let error_message =
match assignment_type with
| PassingParamToFunction function_info ->
Format.sprintf "%s%s"
(mk_description_for_bad_param_passed function_info ~nullability_evidence
~param_nullability_kind:explicit_rhs_nullable_kind)
alternative_recommendation
| AssigningToField field_name ->
let rhs_description =
match explicit_rhs_nullable_kind with
| ErrorRenderingUtils.UserFriendlyNullable.Null ->
"`null`"
| ErrorRenderingUtils.UserFriendlyNullable.Nullable ->
"a nullable"
in
Format.asprintf "%a is declared non-nullable but is assigned %s%s.%s" MF.pp_monospaced
(Fieldname.get_field_name field_name)
rhs_description nullability_evidence_as_suffix alternative_recommendation
| ReturningFromFunction function_proc_name ->
let return_description =
match explicit_rhs_nullable_kind with
| ErrorRenderingUtils.UserFriendlyNullable.Null ->
(* Return `null` in all_whitelisted branches *)
"`null`"
| ErrorRenderingUtils.UserFriendlyNullable.Nullable ->
"a nullable value"
in
Format.asprintf "%a: return type is declared non-nullable but the method returns %s%s.%s"
MF.pp_monospaced
(Procname.to_simplified_string ~withclass:false function_proc_name)
return_description nullability_evidence_as_suffix alternative_recommendation
in
let issue_type = get_issue_type assignment_type in
(error_message, issue_type, assignment_location)
let get_description ~assignment_location assignment_type ~rhs_origin let get_description ~assignment_location assignment_type ~rhs_origin
{nullsafe_mode; violation= {rhs}} = {nullsafe_mode; violation= {rhs}} =
let special_message = let user_friendly_nullable =
if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then ErrorRenderingUtils.UserFriendlyNullable.from_nullability rhs
ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:rhs |> IOption.if_none_eval ~f:(fun () ->
~bad_usage_location:assignment_location rhs_origin Logging.die InternalError
else None "get_description:: Assignment violation should not be possible for non-nullable \
values on right hand side" )
in in
match special_message with match user_friendly_nullable with
| Some desc -> | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind ->
desc (* Attempt to assigning a value which is not explictly declared as nullable,
| _ -> but still can not be trusted in this particular mode.
let nullability_evidence = *)
get_origin_opt assignment_type rhs_origin ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind
|> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin) ~bad_usage_location:assignment_location rhs_origin
in | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind ->
let nullability_evidence_as_suffix = (* Attempt to assigning a value that can be explained to the user as nullable. *)
Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" mk_nullsafe_issue_for_explicitly_nullable_values ~assignment_type ~rhs_origin
in ~explicit_rhs_nullable_kind:explicit_kind ~assignment_location
let module MF = MarkupFormatter in
let alternative_method_description =
ErrorRenderingUtils.find_alternative_nonnull_method_description rhs_origin
in
let alternative_recommendation =
Option.value_map alternative_method_description
~f:(fun descr ->
Format.asprintf " If you don't expect null, use %a instead." MF.pp_monospaced descr )
~default:""
in
let error_message =
match assignment_type with
| PassingParamToFunction function_info ->
Format.sprintf "%s%s"
(mk_description_for_bad_param_passed function_info ~nullability_evidence
~param_nullability:rhs)
alternative_recommendation
| AssigningToField field_name ->
let rhs_description =
Nullability.(
match rhs with
| Null ->
"`null`"
| Nullable ->
"a nullable"
| other ->
Logging.die InternalError
"violation_description(assign_field):: invariant violation: unexpected \
nullability %a"
Nullability.pp other)
in
Format.asprintf "%a is declared non-nullable but is assigned %s%s.%s" MF.pp_monospaced
(Fieldname.get_field_name field_name)
rhs_description nullability_evidence_as_suffix alternative_recommendation
| ReturningFromFunction function_proc_name ->
let return_description =
Nullability.(
match rhs with
| Null ->
(* Return `null` in all_whitelisted branches *)
"`null`"
| Nullable ->
"a nullable value"
| other ->
Logging.die InternalError
"violation_description(ret_fun):: invariant violation: unexpected \
nullability %a"
Nullability.pp other)
in
Format.asprintf
"%a: return type is declared non-nullable but the method returns %s%s.%s"
MF.pp_monospaced
(Procname.to_simplified_string ~withclass:false function_proc_name)
return_description nullability_evidence_as_suffix alternative_recommendation
in
let issue_type = get_issue_type assignment_type in
(error_message, issue_type, assignment_location)
end end
let check ~lhs ~rhs = let check ~lhs ~rhs =

@ -34,79 +34,87 @@ module ReportableViolation = struct
if should_show_origin then Some origin else None if should_show_origin then Some origin else None
let mk_nullsafe_issue_for_explicitly_nullable_values ~explicit_kind ~dereference_type
dereference_location ~nullable_object_descr ~nullable_object_origin =
let module MF = MarkupFormatter in
let what_is_dereferred_str =
match dereference_type with
| MethodCall _ | AccessToField _ -> (
match nullable_object_descr with
| None ->
"Object"
(* Just describe an object itself *)
| Some descr ->
MF.monospaced_to_string descr )
| ArrayLengthAccess | AccessByIndex _ -> (
(* In Java, those operations can be applied only to arrays *)
match nullable_object_descr with
| None ->
"Array"
| Some descr ->
Format.sprintf "Array %s" (MF.monospaced_to_string descr) )
in
let action_descr =
match dereference_type with
| MethodCall method_name ->
Format.sprintf "calling %s"
(MF.monospaced_to_string (Procname.to_simplified_string method_name))
| AccessToField field_name ->
Format.sprintf "accessing field %s"
(MF.monospaced_to_string (Fieldname.to_simplified_string field_name))
| AccessByIndex {index_desc} ->
Format.sprintf "accessing at index %s" (MF.monospaced_to_string index_desc)
| ArrayLengthAccess ->
"accessing its length"
in
let origin_descr =
get_origin_opt ~nullable_object_descr nullable_object_origin
|> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin)
|> Option.value_map ~f:(fun origin -> ": " ^ origin) ~default:""
in
let alternative_method_description =
ErrorRenderingUtils.find_alternative_nonnull_method_description nullable_object_origin
in
let alternative_recommendation =
Option.value_map alternative_method_description
~f:(fun descr ->
Format.asprintf " If this is intentional, use %a instead." MF.pp_monospaced descr )
~default:""
in
let description =
match explicit_kind with
| ErrorRenderingUtils.UserFriendlyNullable.Null ->
Format.sprintf
"NullPointerException will be thrown at this line! %s is `null` and is dereferenced \
via %s%s."
what_is_dereferred_str action_descr origin_descr
| ErrorRenderingUtils.UserFriendlyNullable.Nullable ->
Format.sprintf "%s is nullable and is not locally checked for null when %s%s.%s"
what_is_dereferred_str action_descr origin_descr alternative_recommendation
in
(description, IssueType.eradicate_nullable_dereference, dereference_location)
let get_description {nullsafe_mode; violation= {nullability}} ~dereference_location let get_description {nullsafe_mode; violation= {nullability}} ~dereference_location
dereference_type ~nullable_object_descr ~nullable_object_origin = dereference_type ~nullable_object_descr ~nullable_object_origin =
let module MF = MarkupFormatter in let user_friendly_nullable =
let special_message = ErrorRenderingUtils.UserFriendlyNullable.from_nullability nullability
if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then |> IOption.if_none_eval ~f:(fun () ->
ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:nullability Logging.die InternalError
~bad_usage_location:dereference_location nullable_object_origin "get_description:: Dereference violation should not be possible for non-nullable \
else None values" )
in in
match special_message with match user_friendly_nullable with
| Some desc -> | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind ->
desc (* Attempt to dereference a value which is not explictly declared as nullable,
| _ -> but still can not be trusted in this particular mode.
let what_is_dereferred_str = *)
match dereference_type with ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind
| MethodCall _ | AccessToField _ -> ( ~bad_usage_location:dereference_location nullable_object_origin
match nullable_object_descr with | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind ->
| None -> (* Attempt to dereference value that can be explained to the user as nullable. *)
"Object" mk_nullsafe_issue_for_explicitly_nullable_values ~explicit_kind ~dereference_type
(* Just describe an object itself *) dereference_location ~nullable_object_descr ~nullable_object_origin
| Some descr ->
MF.monospaced_to_string descr )
| ArrayLengthAccess | AccessByIndex _ -> (
(* In Java, those operations can be applied only to arrays *)
match nullable_object_descr with
| None ->
"Array"
| Some descr ->
Format.sprintf "Array %s" (MF.monospaced_to_string descr) )
in
let action_descr =
match dereference_type with
| MethodCall method_name ->
Format.sprintf "calling %s"
(MF.monospaced_to_string (Procname.to_simplified_string method_name))
| AccessToField field_name ->
Format.sprintf "accessing field %s"
(MF.monospaced_to_string (Fieldname.to_simplified_string field_name))
| AccessByIndex {index_desc} ->
Format.sprintf "accessing at index %s" (MF.monospaced_to_string index_desc)
| ArrayLengthAccess ->
"accessing its length"
in
let origin_descr =
get_origin_opt ~nullable_object_descr nullable_object_origin
|> Option.bind ~f:(fun origin -> TypeOrigin.get_description origin)
|> Option.value_map ~f:(fun origin -> ": " ^ origin) ~default:""
in
let alternative_method_description =
ErrorRenderingUtils.find_alternative_nonnull_method_description nullable_object_origin
in
let alternative_recommendation =
Option.value_map alternative_method_description
~f:(fun descr ->
Format.asprintf " If this is intentional, use %a instead." MF.pp_monospaced descr )
~default:""
in
let description =
match nullability with
| Nullability.Null ->
Format.sprintf
"NullPointerException will be thrown at this line! %s is `null` and is \
dereferenced via %s%s."
what_is_dereferred_str action_descr origin_descr
| Nullability.Nullable ->
Format.sprintf "%s is nullable and is not locally checked for null when %s%s.%s"
what_is_dereferred_str action_descr origin_descr alternative_recommendation
| other ->
Logging.die InternalError
"violation_description:: invariant violation: unexpected nullability %a"
Nullability.pp other
in
(description, IssueType.eradicate_nullable_dereference, dereference_location)
let get_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode let get_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode

@ -8,6 +8,28 @@
open! IStd open! IStd
module F = Format module F = Format
module UserFriendlyNullable = struct
type t = ExplainablyNullable of explainably_nullable_kind | UntrustedNonnull of untrusted_kind
and explainably_nullable_kind = Nullable | Null
and untrusted_kind = ThirdPartyNonnull | UncheckedNonnull | LocallyCheckedNonnull
let from_nullability = function
| Nullability.Nullable ->
Some (ExplainablyNullable Nullable)
| Nullability.Null ->
Some (ExplainablyNullable Null)
| Nullability.UncheckedNonnull ->
Some (UntrustedNonnull UncheckedNonnull)
| Nullability.LocallyCheckedNonnull ->
Some (UntrustedNonnull LocallyCheckedNonnull)
| Nullability.ThirdPartyNonnull ->
Some (UntrustedNonnull ThirdPartyNonnull)
| Nullability.StrictNonnull ->
None
end
let is_object_nullability_self_explanatory ~object_expression (object_origin : TypeOrigin.t) = let is_object_nullability_self_explanatory ~object_expression (object_origin : TypeOrigin.t) =
(* Fundamentally, object can be of two kinds: (* Fundamentally, object can be of two kinds:
1. Indirect: local variable that was instantiated before. 1. Indirect: local variable that was instantiated before.
@ -83,28 +105,40 @@ let get_field_class_name field_name =
|> Option.value_map ~f:(fun (classname, _) -> classname) ~default:"the field class" |> Option.value_map ~f:(fun (classname, _) -> classname) ~default:"the field class"
let mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode nullability = let mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind =
match (nullsafe_mode, nullability) with match (nullsafe_mode, untrusted_kind) with
| NullsafeMode.Strict, Nullability.UncheckedNonnull -> | NullsafeMode.Strict, UserFriendlyNullable.UncheckedNonnull ->
"non-strict classes" "non-strict classes"
| NullsafeMode.Strict, Nullability.LocallyCheckedNonnull -> | NullsafeMode.Strict, UserFriendlyNullable.LocallyCheckedNonnull ->
"nullsafe-local classes" "nullsafe-local classes"
| NullsafeMode.Local _, Nullability.UncheckedNonnull -> | NullsafeMode.Local _, _ ->
"non-nullsafe classes" "non-nullsafe classes"
| _ -> | NullsafeMode.Default, _ ->
Logging.die Logging.InternalError Logging.die InternalError
"Should be called only for locally checked or unchecked nonnull cases" "mk_coming_from_unchecked_or_locally_checked_case_only:: not applicable to default mode"
| _, UserFriendlyNullable.ThirdPartyNonnull ->
Logging.die InternalError
"mk_coming_from_unchecked_or_locally_checked_case_only:: not applicable to \
ThirdPartyNonnull case"
let mk_recommendation nullsafe_mode nullability what = let mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind
match (nullsafe_mode, nullability) with ~what_to_strictify =
| NullsafeMode.Strict, Nullability.UncheckedNonnull match untrusted_kind with
| NullsafeMode.Strict, Nullability.LocallyCheckedNonnull -> | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> (
Some (F.sprintf "make %s nullsafe strict" what) match nullsafe_mode with
| NullsafeMode.Local _, Nullability.UncheckedNonnull -> | NullsafeMode.Strict ->
Some (F.sprintf "make %s nullsafe" what) F.sprintf "make %s nullsafe strict" what_to_strictify
| _ -> | NullsafeMode.Local _ ->
None F.sprintf "make %s nullsafe" what_to_strictify
| NullsafeMode.Default ->
Logging.die InternalError
"mk_recommendation_unchecked_or_locally_checked_case_only:: should not be called for \
default mode" )
| UserFriendlyNullable.ThirdPartyNonnull ->
Logging.die InternalError
"mk_recommendation_unchecked_or_locally_checked_case_only:: not applicable to \
ThirdPartyNonnull case"
let mk_recommendation_for_third_party_field nullsafe_mode field = let mk_recommendation_for_third_party_field nullsafe_mode field =
@ -114,12 +148,12 @@ let mk_recommendation_for_third_party_field nullsafe_mode field =
| NullsafeMode.Local _ -> | NullsafeMode.Local _ ->
F.sprintf "access %s via a nullsafe getter" field F.sprintf "access %s via a nullsafe getter" field
| NullsafeMode.Default -> | NullsafeMode.Default ->
Logging.die Logging.InternalError Logging.die InternalError
"Should not happen: we should tolerate third party in default mode" "mk_recommendation_for_third_party_field:: Should not happen: we should tolerate third \
party in default mode"
let get_info object_origin nullsafe_mode bad_nullability = let get_info object_origin nullsafe_mode untrusted_kind =
let open IOption.Let_syntax in
match object_origin with match object_origin with
| TypeOrigin.MethodCall {pname; call_loc} -> | TypeOrigin.MethodCall {pname; call_loc} ->
let offending_object = let offending_object =
@ -128,14 +162,9 @@ let get_info object_origin nullsafe_mode bad_nullability =
in in
let object_loc = call_loc in let object_loc = call_loc in
let what_is_used = "Result of this call" in let what_is_used = "Result of this call" in
let+ coming_from_explanation, recommendation, issue_type = let coming_from_explanation, recommendation, issue_type =
match bad_nullability with match untrusted_kind with
| Nullability.Null | Nullability.Nullable -> | UserFriendlyNullable.ThirdPartyNonnull ->
(* This method makes sense only for non-nullable violations *)
None
| Nullability.StrictNonnull ->
Logging.die InternalError "There should not be type violations involving StrictNonnull"
| Nullability.ThirdPartyNonnull ->
let suggested_third_party_sig_file = let suggested_third_party_sig_file =
ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc
(ThirdPartyAnnotationGlobalRepo.get_repo ()) (ThirdPartyAnnotationGlobalRepo.get_repo ())
@ -149,19 +178,19 @@ let get_info object_origin nullsafe_mode bad_nullability =
(* this can happen when third party is registered in a deprecated way (not in third party repository) *) (* this can happen when third party is registered in a deprecated way (not in third party repository) *)
~default:"the third party signature storage" ~default:"the third party signature storage"
in in
return ( "not vetted third party methods"
( "not vetted third party methods" , F.sprintf "add the correct signature to %s" where_to_add_signature
, F.sprintf "add the correct signature to %s" where_to_add_signature , IssueType.eradicate_unvetted_third_party_in_nullsafe )
, IssueType.eradicate_unvetted_third_party_in_nullsafe ) | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull ->
| Nullability.UncheckedNonnull | Nullability.LocallyCheckedNonnull ->
let from = let from =
mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode bad_nullability mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind
in in
let+ recommendation = let recommendation =
let what_to_strictify = let what_to_strictify =
Option.value (get_method_class_name pname) ~default:offending_object Option.value (get_method_class_name pname) ~default:offending_object
in in
mk_recommendation nullsafe_mode bad_nullability what_to_strictify mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode
untrusted_kind ~what_to_strictify
in in
let issue_type = IssueType.eradicate_unchecked_usage_in_nullsafe in let issue_type = IssueType.eradicate_unchecked_usage_in_nullsafe in
(from, recommendation, issue_type) (from, recommendation, issue_type)
@ -183,24 +212,19 @@ let get_info object_origin nullsafe_mode bad_nullability =
(* TODO: currently we do not support third-party annotations for fields. Because of this, (* TODO: currently we do not support third-party annotations for fields. Because of this,
render error like it is a non-stict class. *) render error like it is a non-stict class. *)
let what_is_used = "This field" in let what_is_used = "This field" in
let+ coming_from_explanation, recommendation, issue_type = let coming_from_explanation, recommendation, issue_type =
match bad_nullability with match untrusted_kind with
| Nullability.Null | Nullability.Nullable -> | UserFriendlyNullable.ThirdPartyNonnull ->
(* This method makes sense only for non-nullable violations *) ( "third-party classes"
None , mk_recommendation_for_third_party_field nullsafe_mode unqualified_name
| Nullability.StrictNonnull -> , IssueType.eradicate_unvetted_third_party_in_nullsafe )
Logging.die InternalError "There should not be type violations involving StrictNonnull" | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull ->
| Nullability.ThirdPartyNonnull ->
return
( "third-party classes"
, mk_recommendation_for_third_party_field nullsafe_mode unqualified_name
, IssueType.eradicate_unvetted_third_party_in_nullsafe )
| Nullability.UncheckedNonnull | Nullability.LocallyCheckedNonnull ->
let from = let from =
mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode bad_nullability mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind
in in
let+ recommendation = let recommendation =
mk_recommendation nullsafe_mode bad_nullability (get_field_class_name field_name) mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode
untrusted_kind ~what_to_strictify:(get_field_class_name field_name)
in in
(from, recommendation, IssueType.eradicate_unchecked_usage_in_nullsafe) (from, recommendation, IssueType.eradicate_unchecked_usage_in_nullsafe)
in in
@ -210,19 +234,22 @@ let get_info object_origin nullsafe_mode bad_nullability =
; what_is_used ; what_is_used
; recommendation ; recommendation
; issue_type } ; issue_type }
| _ -> | other ->
None Logging.die InternalError
"get_info:: untrusted_kind is possible only for MethodCall and Field origins, got %s \
instead"
(TypeOrigin.to_string other)
let mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability ~bad_usage_location object_origin = let mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind ~bad_usage_location
let open IOption.Let_syntax in object_origin =
let+ { offending_object let { offending_object
; object_loc ; object_loc
; coming_from_explanation ; coming_from_explanation
; what_is_used ; what_is_used
; recommendation ; recommendation
; issue_type } = ; issue_type } =
get_info object_origin nullsafe_mode bad_nullability get_info object_origin nullsafe_mode untrusted_kind
in in
let description = let description =
F.asprintf F.asprintf

@ -9,22 +9,44 @@
open! IStd open! IStd
(** "Effectively nullable values" from the user perspective. Depending on context, convention, and
mode, Nullsafe treats such and such things as nullable or non-null. At some point this needs to
be explain to the user. *)
module UserFriendlyNullable : sig
type t =
| ExplainablyNullable of explainably_nullable_kind
(** Value that is nullable according to nullsafe semantics and conventions. It can be
nullable because of an explicit annotation, models, default nullability conventions,
etc. *)
| UntrustedNonnull of untrusted_kind
(** Value is not nullable per se, but we still can not treat it as non-null in current mode.
From the user perspective, it is a very different case: violations of this type need to
be explained in a way so that it is clear why exactly can not nullsafe trust it in this
context. *)
and explainably_nullable_kind = Nullable | Null
and untrusted_kind = ThirdPartyNonnull | UncheckedNonnull | LocallyCheckedNonnull
val from_nullability : Nullability.t -> t option
end
val is_object_nullability_self_explanatory : object_expression:string -> TypeOrigin.t -> bool val is_object_nullability_self_explanatory : object_expression:string -> TypeOrigin.t -> bool
(** In order to understand why such and such object is nullable (or not nullable), we render its (** In order to understand why such and such object is nullable (or not nullable), we render its
origin. In some cases this is redundant and adds extra noise for the user. *) origin. In some cases this is redundant and adds extra noise for the user. *)
val mk_special_nullsafe_issue : val mk_nullsafe_issue_for_untrusted_values :
nullsafe_mode:NullsafeMode.t nullsafe_mode:NullsafeMode.t
-> bad_nullability:Nullability.t -> untrusted_kind:UserFriendlyNullable.untrusted_kind
-> bad_usage_location:Location.t -> bad_usage_location:Location.t
-> TypeOrigin.t -> TypeOrigin.t
-> (string * IssueType.t * Location.t) option -> string * IssueType.t * Location.t
(** Situation when we tried to use nonnull values in a nullsafe mode that does not trust them to be (** Situation when we tried to use nonnull values in a nullsafe mode that does not trust them to be
non-nullable. From the user perspective, this case is different from normal nullable assignment non-nullable: [untrusted_kind]. From the user perspective, this case is different from normal
or dereference violation: what needs to be described is why does not this mode trust this value nullable assignment or dereference violation: what needs to be described is why does not this
(and what are possible actions). Returns a tuple (error message, issue type, error location). mode trust this value (and what are possible actions). Returns a tuple (error message, issue
NOTE: Location of the error will be NOT in the place when the value is used (that is type, error location). NOTE: Location of the error will be NOT in the place when the value is
[bad_usage_location]), but where the value is first obtained from. *) used (that is [bad_usage_location]), but where the value is first obtained from. *)
val find_alternative_nonnull_method_description : TypeOrigin.t -> string option val find_alternative_nonnull_method_description : TypeOrigin.t -> string option
(** If type origin is the result of a nullable method call that have a known nonnullable alternative (** If type origin is the result of a nullable method call that have a known nonnullable alternative

Loading…
Cancel
Save