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.
105 lines
4.5 KiB
105 lines
4.5 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
|
|
|
|
(* TODO(T54088319) get rid of annotation_deprecated:
|
|
Introduce "field flags" and move all other usages to this dedicated datatype
|
|
*)
|
|
type t = {annotation_deprecated: Annot.Item.t; annotated_type: AnnotatedType.t}
|
|
|
|
let rec get_type_name {Typ.desc} =
|
|
match desc with Typ.Tstruct name -> Some name | Typ.Tptr (t, _) -> get_type_name t | _ -> None
|
|
|
|
|
|
(* A heuristic to guess if the field is actually a Java enum value. *)
|
|
let is_enum_value tenv ~class_typ (field_info : Struct.field_info) =
|
|
(* It is tricky to get this information with 100% precision, but this works in most of
|
|
practical cases.
|
|
In Java, enums are special classes, and enum values are (implicitly generated) static fields in these classes,
|
|
which are initialized statically via calling the class constructor.
|
|
Though there can be OTHER (user-defined) static fields in the same enum class,
|
|
this is a rare case.*)
|
|
if not field_info.is_static then false
|
|
else
|
|
match (get_type_name class_typ, get_type_name field_info.typ) with
|
|
(* enums values are fields which type is the same as the type of the enum class *)
|
|
| Some class_name, Some field_type_name
|
|
when Typ.equal_name class_name field_type_name && PatternMatch.Java.is_enum tenv class_name ->
|
|
true
|
|
(* Could not fetch one of the class names, or they are different. Should not happen for enum values. *)
|
|
| _ ->
|
|
false
|
|
|
|
|
|
let is_synthetic field_name = String.contains field_name '$'
|
|
|
|
(* For the special mode, return the provisionally nullable annotation, otherwise return the unchaged nullability *)
|
|
let maybe_provisionally_nullable field_name ~field_class ~class_under_analysis nullability =
|
|
if
|
|
Config.nullsafe_annotation_graph
|
|
(* Provisionally nullable mode distinct "internal" fields in the class and all the fields outside *)
|
|
&& Typ.Name.equal field_class class_under_analysis
|
|
&& AnnotatedNullability.can_be_considered_for_provisional_annotation nullability
|
|
then AnnotatedNullability.ProvisionallyNullable (ProvisionalAnnotation.Field {field_name})
|
|
else nullability
|
|
|
|
|
|
let get tenv field_name ~class_typ ~class_under_analysis =
|
|
let open IOption.Let_syntax in
|
|
let lookup = Tenv.lookup tenv in
|
|
(* We currently don't support field-level strict mode annotation, so fetch it from class *)
|
|
let nullsafe_mode =
|
|
Typ.name class_typ
|
|
|> Option.value_map
|
|
~f:(fun class_name ->
|
|
Typ.Name.Java.get_java_class_name_exn class_name |> NullsafeMode.of_class tenv )
|
|
~default:NullsafeMode.Default
|
|
in
|
|
let is_third_party =
|
|
ThirdPartyAnnotationInfo.is_third_party_typ
|
|
(ThirdPartyAnnotationGlobalRepo.get_repo ())
|
|
class_typ
|
|
in
|
|
let+ (Struct.{typ= field_typ; annotations} as field_info) =
|
|
Struct.get_field_info ~lookup field_name class_typ
|
|
in
|
|
let is_enum_value = is_enum_value tenv ~class_typ field_info in
|
|
let nullability =
|
|
(* TODO(T62825735): support trusted callees for fields *)
|
|
AnnotatedNullability.of_type_and_annotation ~is_callee_in_trust_list:false ~nullsafe_mode
|
|
~is_third_party field_typ annotations
|
|
in
|
|
let special_case_nullability =
|
|
if Nullability.is_nonnullish (AnnotatedNullability.get_nullability nullability) then
|
|
if
|
|
is_enum_value
|
|
(* Enum values are the special case - they can not be null. So we can strengten nullability.
|
|
Note that if it is nullable, we do NOT change nullability: in this case this is probably
|
|
not an enum value, but just a static field annotated as nullable.
|
|
*)
|
|
then AnnotatedNullability.StrictNonnull EnumValue
|
|
else if is_synthetic (Fieldname.get_field_name field_name) then
|
|
(* This field is artifact of codegen and is not visible to the user.
|
|
Surfacing it as non-strict is non-actionable for the user *)
|
|
AnnotatedNullability.StrictNonnull SyntheticField
|
|
else if Models.is_field_nonnullable field_name then
|
|
AnnotatedNullability.StrictNonnull ModelledNonnull
|
|
else nullability
|
|
else nullability
|
|
in
|
|
let field_class =
|
|
Option.value_exn (get_type_name class_typ)
|
|
~message:"otherwise we would not have fetched field info above"
|
|
in
|
|
let final_nullability =
|
|
maybe_provisionally_nullable field_name ~field_class ~class_under_analysis
|
|
special_case_nullability
|
|
in
|
|
let annotated_type = AnnotatedType.{nullability= final_nullability; typ= field_typ} in
|
|
{annotation_deprecated= annotations; annotated_type}
|