diff --git a/infer/src/nullsafe/AssignmentRule.ml b/infer/src/nullsafe/AssignmentRule.ml index 40a14837b..58833608f 100644 --- a/infer/src/nullsafe/AssignmentRule.ml +++ b/infer/src/nullsafe/AssignmentRule.ml @@ -63,7 +63,7 @@ module ReportableViolation = struct let mk_description_for_bad_param_passed {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 = Option.value_map nullability_evidence ~f:(fun evidence -> ": " ^ evidence) ~default:"" in @@ -73,15 +73,11 @@ module ReportableViolation = struct if String.equal actual_param_expression "null" then "is `null`" else let nullability_descr = - match param_nullability with - | Nullability.Null -> + match param_nullability_kind with + | ErrorRenderingUtils.UserFriendlyNullable.Null -> "`null`" - | Nullability.Nullable -> + | ErrorRenderingUtils.UserFriendlyNullable.Nullable -> "nullable" - | other -> - Logging.die InternalError - "mk_description_for_bad_param:: invariant violation: unexpected nullability %a" - Nullability.pp other in Format.asprintf "%a is %s" MF.pp_monospaced actual_param_expression nullability_descr in @@ -147,82 +143,81 @@ module ReportableViolation = struct 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 {nullsafe_mode; violation= {rhs}} = - let special_message = - if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then - ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:rhs - ~bad_usage_location:assignment_location rhs_origin - else None + let user_friendly_nullable = + ErrorRenderingUtils.UserFriendlyNullable.from_nullability rhs + |> IOption.if_none_eval ~f:(fun () -> + Logging.die InternalError + "get_description:: Assignment violation should not be possible for non-nullable \ + values on right hand side" ) in - match special_message with - | Some desc -> - desc - | _ -> - 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: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) + match user_friendly_nullable with + | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind -> + (* Attempt to assigning a value which is not explictly declared as nullable, + but still can not be trusted in this particular mode. + *) + ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind + ~bad_usage_location:assignment_location rhs_origin + | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind -> + (* Attempt to assigning a value that can be explained to the user as nullable. *) + mk_nullsafe_issue_for_explicitly_nullable_values ~assignment_type ~rhs_origin + ~explicit_rhs_nullable_kind:explicit_kind ~assignment_location end let check ~lhs ~rhs = diff --git a/infer/src/nullsafe/DereferenceRule.ml b/infer/src/nullsafe/DereferenceRule.ml index 206caf4d1..55570635d 100644 --- a/infer/src/nullsafe/DereferenceRule.ml +++ b/infer/src/nullsafe/DereferenceRule.ml @@ -34,79 +34,87 @@ module ReportableViolation = struct 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 dereference_type ~nullable_object_descr ~nullable_object_origin = - let module MF = MarkupFormatter in - let special_message = - if not (NullsafeMode.equal NullsafeMode.Default nullsafe_mode) then - ErrorRenderingUtils.mk_special_nullsafe_issue ~nullsafe_mode ~bad_nullability:nullability - ~bad_usage_location:dereference_location nullable_object_origin - else None + let user_friendly_nullable = + ErrorRenderingUtils.UserFriendlyNullable.from_nullability nullability + |> IOption.if_none_eval ~f:(fun () -> + Logging.die InternalError + "get_description:: Dereference violation should not be possible for non-nullable \ + values" ) in - match special_message with - | Some desc -> - desc - | _ -> - 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 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) + match user_friendly_nullable with + | ErrorRenderingUtils.UserFriendlyNullable.UntrustedNonnull untrusted_kind -> + (* Attempt to dereference a value which is not explictly declared as nullable, + but still can not be trusted in this particular mode. + *) + ErrorRenderingUtils.mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind + ~bad_usage_location:dereference_location nullable_object_origin + | ErrorRenderingUtils.UserFriendlyNullable.ExplainablyNullable explicit_kind -> + (* Attempt to dereference value that can be explained to the user as nullable. *) + mk_nullsafe_issue_for_explicitly_nullable_values ~explicit_kind ~dereference_type + dereference_location ~nullable_object_descr ~nullable_object_origin let get_severity {nullsafe_mode} = NullsafeMode.severity nullsafe_mode diff --git a/infer/src/nullsafe/ErrorRenderingUtils.ml b/infer/src/nullsafe/ErrorRenderingUtils.ml index b973798c0..cb297a7fd 100644 --- a/infer/src/nullsafe/ErrorRenderingUtils.ml +++ b/infer/src/nullsafe/ErrorRenderingUtils.ml @@ -8,6 +8,28 @@ open! IStd 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) = (* Fundamentally, object can be of two kinds: 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" -let mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode nullability = - match (nullsafe_mode, nullability) with - | NullsafeMode.Strict, Nullability.UncheckedNonnull -> +let mk_coming_from_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind = + match (nullsafe_mode, untrusted_kind) with + | NullsafeMode.Strict, UserFriendlyNullable.UncheckedNonnull -> "non-strict classes" - | NullsafeMode.Strict, Nullability.LocallyCheckedNonnull -> + | NullsafeMode.Strict, UserFriendlyNullable.LocallyCheckedNonnull -> "nullsafe-local classes" - | NullsafeMode.Local _, Nullability.UncheckedNonnull -> + | NullsafeMode.Local _, _ -> "non-nullsafe classes" - | _ -> - Logging.die Logging.InternalError - "Should be called only for locally checked or unchecked nonnull cases" + | NullsafeMode.Default, _ -> + Logging.die InternalError + "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 = - match (nullsafe_mode, nullability) with - | NullsafeMode.Strict, Nullability.UncheckedNonnull - | NullsafeMode.Strict, Nullability.LocallyCheckedNonnull -> - Some (F.sprintf "make %s nullsafe strict" what) - | NullsafeMode.Local _, Nullability.UncheckedNonnull -> - Some (F.sprintf "make %s nullsafe" what) - | _ -> - None +let mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode untrusted_kind + ~what_to_strictify = + match untrusted_kind with + | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> ( + match nullsafe_mode with + | NullsafeMode.Strict -> + F.sprintf "make %s nullsafe strict" what_to_strictify + | NullsafeMode.Local _ -> + 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 = @@ -114,12 +148,12 @@ let mk_recommendation_for_third_party_field nullsafe_mode field = | NullsafeMode.Local _ -> F.sprintf "access %s via a nullsafe getter" field | NullsafeMode.Default -> - Logging.die Logging.InternalError - "Should not happen: we should tolerate third party in default mode" + Logging.die InternalError + "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 open IOption.Let_syntax in +let get_info object_origin nullsafe_mode untrusted_kind = match object_origin with | TypeOrigin.MethodCall {pname; call_loc} -> let offending_object = @@ -128,14 +162,9 @@ let get_info object_origin nullsafe_mode bad_nullability = in let object_loc = call_loc in let what_is_used = "Result of this call" in - let+ coming_from_explanation, recommendation, issue_type = - match bad_nullability with - | Nullability.Null | Nullability.Nullable -> - (* 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 coming_from_explanation, recommendation, issue_type = + match untrusted_kind with + | UserFriendlyNullable.ThirdPartyNonnull -> let suggested_third_party_sig_file = ThirdPartyAnnotationInfo.lookup_related_sig_file_for_proc (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) *) ~default:"the third party signature storage" in - return - ( "not vetted third party methods" - , F.sprintf "add the correct signature to %s" where_to_add_signature - , IssueType.eradicate_unvetted_third_party_in_nullsafe ) - | Nullability.UncheckedNonnull | Nullability.LocallyCheckedNonnull -> + ( "not vetted third party methods" + , F.sprintf "add the correct signature to %s" where_to_add_signature + , IssueType.eradicate_unvetted_third_party_in_nullsafe ) + | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> 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 - let+ recommendation = + let recommendation = let what_to_strictify = Option.value (get_method_class_name pname) ~default:offending_object 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 let issue_type = IssueType.eradicate_unchecked_usage_in_nullsafe in (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, render error like it is a non-stict class. *) let what_is_used = "This field" in - let+ coming_from_explanation, recommendation, issue_type = - match bad_nullability with - | Nullability.Null | Nullability.Nullable -> - (* 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 -> - 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 coming_from_explanation, recommendation, issue_type = + match untrusted_kind with + | UserFriendlyNullable.ThirdPartyNonnull -> + ( "third-party classes" + , mk_recommendation_for_third_party_field nullsafe_mode unqualified_name + , IssueType.eradicate_unvetted_third_party_in_nullsafe ) + | UserFriendlyNullable.UncheckedNonnull | UserFriendlyNullable.LocallyCheckedNonnull -> 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 - let+ recommendation = - mk_recommendation nullsafe_mode bad_nullability (get_field_class_name field_name) + let recommendation = + mk_strictification_advice_unchecked_or_locally_checked_case_only nullsafe_mode + untrusted_kind ~what_to_strictify:(get_field_class_name field_name) in (from, recommendation, IssueType.eradicate_unchecked_usage_in_nullsafe) in @@ -210,19 +234,22 @@ let get_info object_origin nullsafe_mode bad_nullability = ; what_is_used ; recommendation ; issue_type } - | _ -> - None + | other -> + 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 open IOption.Let_syntax in - let+ { offending_object - ; object_loc - ; coming_from_explanation - ; what_is_used - ; recommendation - ; issue_type } = - get_info object_origin nullsafe_mode bad_nullability +let mk_nullsafe_issue_for_untrusted_values ~nullsafe_mode ~untrusted_kind ~bad_usage_location + object_origin = + let { offending_object + ; object_loc + ; coming_from_explanation + ; what_is_used + ; recommendation + ; issue_type } = + get_info object_origin nullsafe_mode untrusted_kind in let description = F.asprintf diff --git a/infer/src/nullsafe/ErrorRenderingUtils.mli b/infer/src/nullsafe/ErrorRenderingUtils.mli index 609b8ef53..6e6d40033 100644 --- a/infer/src/nullsafe/ErrorRenderingUtils.mli +++ b/infer/src/nullsafe/ErrorRenderingUtils.mli @@ -9,22 +9,44 @@ 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 (** 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. *) -val mk_special_nullsafe_issue : +val mk_nullsafe_issue_for_untrusted_values : nullsafe_mode:NullsafeMode.t - -> bad_nullability:Nullability.t + -> untrusted_kind:UserFriendlyNullable.untrusted_kind -> bad_usage_location:Location.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 - non-nullable. From the user perspective, this case is different from normal nullable assignment - or dereference violation: what needs to be described is why does not this mode trust this value - (and what are possible actions). Returns a tuple (error message, issue type, error location). - NOTE: Location of the error will be NOT in the place when the value is used (that is - [bad_usage_location]), but where the value is first obtained from. *) + non-nullable: [untrusted_kind]. From the user perspective, this case is different from normal + nullable assignment or dereference violation: what needs to be described is why does not this + mode trust this value (and what are possible actions). Returns a tuple (error message, issue + type, error location). NOTE: Location of the error will be NOT in the place when the value is + used (that is [bad_usage_location]), but where the value is first obtained from. *) 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