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.
132 lines
5.2 KiB
132 lines
5.2 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.
|
|
*)
|
|
open! IStd
|
|
|
|
type violation = {nullability: Nullability.t} [@@deriving compare]
|
|
|
|
module ReportableViolation = struct
|
|
type t = {nullsafe_mode: NullsafeMode.t; violation: violation}
|
|
|
|
type dereference_type =
|
|
| MethodCall of Procname.t
|
|
| AccessToField of Fieldname.t
|
|
| AccessByIndex of {index_desc: string}
|
|
| ArrayLengthAccess
|
|
[@@deriving compare]
|
|
|
|
let from nullsafe_mode ({nullability} as violation) =
|
|
if Nullability.is_considered_nonnull ~nullsafe_mode nullability then None
|
|
else Some {nullsafe_mode; violation}
|
|
|
|
|
|
let get_origin_opt ~nullable_object_descr origin =
|
|
let should_show_origin =
|
|
match nullable_object_descr with
|
|
| Some object_expression ->
|
|
not (ErrorRenderingUtils.is_object_nullability_self_explanatory ~object_expression origin)
|
|
| None ->
|
|
true
|
|
in
|
|
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 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 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
|
|
end
|
|
|
|
let check nullability =
|
|
match nullability with
|
|
(* StrictNonnull is the only "real" value that is not null according to type system rules.
|
|
Other values can not be fully trusted.
|
|
*)
|
|
| Nullability.StrictNonnull ->
|
|
Ok ()
|
|
| _ ->
|
|
Error {nullability}
|