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.
139 lines
5.3 KiB
139 lines
5.3 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
|
|
module Hashtbl = Caml.Hashtbl
|
|
open ModelTables
|
|
|
|
(** Unique representation of a method signature in form of a string. *)
|
|
let to_unique_id proc_name = Procname.to_unique_id (Procname.Java proc_name)
|
|
|
|
let match_method_name proc_name name = String.equal (Procname.Java.get_method proc_name) name
|
|
|
|
let table_has_procedure table proc_name =
|
|
let proc_id = to_unique_id proc_name in
|
|
try
|
|
ignore (Hashtbl.find table proc_id) ;
|
|
true
|
|
with Caml.Not_found -> false
|
|
|
|
|
|
let to_modelled_nullability ThirdPartyMethod.{ret_nullability; params} =
|
|
let is_nullable = function
|
|
| ThirdPartyMethod.Nullable ->
|
|
true
|
|
| ThirdPartyMethod.Nonnull ->
|
|
false
|
|
in
|
|
(is_nullable ret_nullability, List.map params ~f:(fun (_, nullability) -> is_nullable nullability))
|
|
|
|
|
|
(* Some methods *)
|
|
let get_special_method_modelled_nullability tenv java_proc_name =
|
|
let open IOption.Let_syntax in
|
|
(* TODO: convert the implementation that does not use PatternMatch *)
|
|
let proc_name = Procname.Java java_proc_name in
|
|
let* class_name = Procname.get_class_type_name proc_name in
|
|
if PatternMatch.Java.is_enum tenv class_name then
|
|
match (Procname.get_method proc_name, Procname.get_parameters proc_name) with
|
|
(* values() is a synthetic enum method that is never null *)
|
|
| "values", [] ->
|
|
Some (false, [])
|
|
(* valueOf() is a synthetic enum method that is never null *)
|
|
| "valueOf", [Procname.Parameter.JavaParameter param_type_name]
|
|
when Typ.equal param_type_name Typ.pointer_to_java_lang_string ->
|
|
Some (false, [false])
|
|
| _ ->
|
|
None
|
|
else None
|
|
|
|
|
|
let get_modelled_annotated_signature ~is_callee_in_trust_list tenv proc_attributes =
|
|
let proc_name =
|
|
Procname.as_java_exn proc_attributes.ProcAttributes.proc_name
|
|
~explanation:"get_modelled_annotated_signature should be called for Java methods only"
|
|
in
|
|
let nullsafe_mode = NullsafeMode.of_java_procname tenv proc_name in
|
|
let annotated_signature =
|
|
AnnotatedSignature.get ~is_callee_in_trust_list ~nullsafe_mode proc_attributes
|
|
in
|
|
let proc_id = to_unique_id proc_name in
|
|
(* Look in the infer internal models *)
|
|
let correct_by_internal_models ann_sig =
|
|
let modelled_nullability =
|
|
(* Look at internal model tables *)
|
|
Hashtbl.find_opt annotated_table_nullability proc_id
|
|
(* Maybe it is a special method whose nullability is predefined *)
|
|
|> IOption.if_none_evalopt ~f:(fun () ->
|
|
get_special_method_modelled_nullability tenv proc_name )
|
|
in
|
|
Option.value_map modelled_nullability
|
|
~f:
|
|
(AnnotatedSignature.set_modelled_nullability (Procname.Java proc_name) ann_sig
|
|
ModelledInternally)
|
|
~default:ann_sig
|
|
in
|
|
(* Look at external models *)
|
|
let correct_by_external_models ann_sig =
|
|
ThirdPartyAnnotationInfo.unique_repr_of_java_proc_name proc_name
|
|
|> ThirdPartyAnnotationInfo.find_nullability_info (ThirdPartyAnnotationGlobalRepo.get_repo ())
|
|
|> Option.map ~f:(fun ThirdPartyAnnotationInfo.{signature; filename; line_number} ->
|
|
(to_modelled_nullability signature, filename, line_number) )
|
|
|> Option.value_map
|
|
(* If we found information in third-party repo, overwrite annotated signature *)
|
|
~f:(fun (modelled_nullability, filename, line_number) ->
|
|
AnnotatedSignature.set_modelled_nullability (Procname.Java proc_name) ann_sig
|
|
(InThirdPartyRepo {filename; line_number})
|
|
modelled_nullability )
|
|
~default:ann_sig
|
|
in
|
|
(* External models overwrite internal ones *)
|
|
annotated_signature |> correct_by_internal_models |> correct_by_external_models
|
|
|
|
|
|
let is_check_not_null proc_name =
|
|
table_has_procedure check_not_null_table proc_name || match_method_name proc_name "checkNotNull"
|
|
|
|
|
|
let get_check_not_null_parameter proc_name =
|
|
let proc_id = to_unique_id proc_name in
|
|
Hashtbl.find_opt check_not_null_parameter_table proc_id
|
|
|
|
|
|
let is_check_state proc_name =
|
|
table_has_procedure check_state_table proc_name || match_method_name proc_name "checkState"
|
|
|
|
|
|
let is_check_argument proc_name =
|
|
table_has_procedure check_argument_table proc_name || match_method_name proc_name "checkArgument"
|
|
|
|
|
|
let is_noreturn proc_name = table_has_procedure noreturn_table proc_name
|
|
|
|
let is_true_on_null proc_name = table_has_procedure true_on_null_table proc_name
|
|
|
|
let is_false_on_null proc_name =
|
|
(* The only usecase for now - consider all overrides of `Object.equals()` correctly
|
|
implementing the Java specification contract (returning false on null). *)
|
|
PatternMatch.Java.is_override_of_lang_object_equals (Procname.Java proc_name)
|
|
|
|
|
|
let is_containsKey proc_name = table_has_procedure containsKey_table proc_name
|
|
|
|
let is_mapPut proc_name = table_has_procedure mapPut_table proc_name
|
|
|
|
let find_nonnullable_alternative proc_name =
|
|
(* NOTE: For now we fetch this info from internal models.
|
|
It is a good idea to support this feature in a user-facing third party repository. *)
|
|
let proc_id = to_unique_id proc_name in
|
|
Hashtbl.find_opt nonnull_alternatives_table proc_id
|
|
|
|
|
|
let is_field_nonnullable field_name =
|
|
Hashtbl.find_opt field_nullability_table (Fieldname.to_full_string field_name)
|
|
|> Option.value_map ~f:(fun is_nullable -> not is_nullable) ~default:false
|