Skip to content

Commit cca0522

Browse files
arthaudfacebook-github-bot
authored andcommitted
Export the explicit annotation of class attributes to pysa
Summary: We need the explicit annotation of class attributes to power the `type_annotation.original.matches/.equals` constraint in our model queries. Let's export it from pyrefly. Reviewed By: tianhan0 Differential Revision: D82984172 fbshipit-source-id: d0bac5af6a9c786d3d95db7f5287ac70c9316ecc
1 parent 8a63415 commit cca0522

File tree

8 files changed

+257
-117
lines changed

8 files changed

+257
-117
lines changed

source/interprocedural/pyreflyApi.ml

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,7 @@ module ModuleInfoFile = struct
843843
type t = {
844844
name: string;
845845
type_: JsonType.t;
846+
explicit_annotation: string option;
846847
location: Location.t option;
847848
}
848849
[@@deriving equal, show]
@@ -852,11 +853,13 @@ module ModuleInfoFile = struct
852853
JsonUtil.get_member json "type"
853854
>>= JsonType.from_json
854855
>>= fun type_ ->
856+
JsonUtil.get_optional_string_member json "explicit_annotation"
857+
>>= fun explicit_annotation ->
855858
JsonUtil.get_optional_string_member json "location"
856859
>>= (function
857860
| Some location -> parse_location location >>| Option.some
858861
| None -> Ok None)
859-
>>| fun location -> { name; type_; location }
862+
>>| fun location -> { name; type_; explicit_annotation; location }
860863
end
861864

862865
module ClassDefinition = struct
@@ -1335,6 +1338,7 @@ module CallableIdToQualifiedNameSharedMemory =
13351338
module ClassField = struct
13361339
type t = {
13371340
type_: PysaType.t;
1341+
explicit_annotation: string option;
13381342
location: Location.t option;
13391343
}
13401344
[@@deriving equal, compare, show]
@@ -2435,8 +2439,13 @@ module ReadWrite = struct
24352439
};
24362440
let fields =
24372441
fields
2438-
|> List.map ~f:(fun { ModuleInfoFile.JsonClassField.name; type_; location } ->
2439-
name, { ClassField.type_ = create_pysa_type type_; location })
2442+
|> List.map
2443+
~f:(fun
2444+
{ ModuleInfoFile.JsonClassField.name; type_; explicit_annotation; location }
2445+
->
2446+
( name,
2447+
{ ClassField.type_ = create_pysa_type type_; explicit_annotation; location }
2448+
))
24402449
|> SerializableStringMap.of_alist_exn
24412450
in
24422451
ClassFieldsSharedMemory.add class_fields_shared_memory qualified_name fields;
@@ -3157,6 +3166,20 @@ module ReadOnly = struct
31573166
|> fun { ClassField.type_; _ } -> type_
31583167

31593168

3169+
let get_class_attribute_explicit_annotation
3170+
{ class_fields_shared_memory; _ }
3171+
~class_name
3172+
~attribute
3173+
=
3174+
ClassFieldsSharedMemory.get
3175+
class_fields_shared_memory
3176+
(FullyQualifiedName.from_reference_unchecked (Reference.create class_name))
3177+
|> assert_shared_memory_key_exists "missing class fields for class"
3178+
|> SerializableStringMap.find_opt attribute
3179+
|> assert_shared_memory_key_exists "missing class field"
3180+
|> fun { ClassField.explicit_annotation; _ } -> explicit_annotation
3181+
3182+
31603183
let get_global_inferred_type { module_globals_shared_memory; _ } ~qualifier ~name =
31613184
ModuleGlobalsSharedMemory.get
31623185
module_globals_shared_memory

source/interprocedural/pyreflyApi.mli

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ module ReadOnly : sig
135135

136136
val get_class_attribute_inferred_type : t -> class_name:string -> attribute:string -> PysaType.t
137137

138+
val get_class_attribute_explicit_annotation
139+
: t ->
140+
class_name:string ->
141+
attribute:string ->
142+
string option
143+
138144
val get_global_inferred_type : t -> qualifier:Ast.Reference.t -> name:string -> PysaType.t option
139145

140146
module Type : sig
@@ -335,6 +341,7 @@ module ModuleInfoFile : sig
335341
type t = {
336342
name: string;
337343
type_: JsonType.t;
344+
explicit_annotation: string option;
338345
location: Ast.Location.t option;
339346
}
340347
[@@deriving equal, show]

source/interprocedural_analyses/taint/modelParseResult.ml

Lines changed: 95 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -877,71 +877,98 @@ end
877877
module TypeAnnotation : sig
878878
type t
879879

880-
val from_original_annotation
881-
: pyre_api:PyrePysaApi.ReadOnly.t ->
882-
preserve_original:bool ->
883-
Expression.t ->
880+
module ExplicitAnnotation : sig
881+
type t =
882+
| Unsupported
883+
| NotFound
884+
| Found of string
885+
end
886+
887+
val from_pyre1 : pyre_api:Analysis.PyrePysaEnvironment.ReadOnly.t -> Expression.t option -> t
888+
889+
val create
890+
: inferred_type:PyrePysaApi.PysaType.t option ->
891+
explicit_annotation:ExplicitAnnotation.t ->
884892
t
885893

886-
val from_pysa_type : PyrePysaApi.PysaType.t -> t
894+
val from_inferred_type : PyrePysaApi.PysaType.t option -> t
887895

888896
val is_annotated : t -> bool
889897

890-
val as_type : t -> PyrePysaApi.PysaType.t
898+
val inferred_type : t -> PyrePysaApi.PysaType.t option
891899

892-
val as_original_annotation : t -> Ast.Expression.t option
900+
val explicit_annotation : t -> ExplicitAnnotation.t
893901

894902
(* Show the original annotation, as written by the user. *)
895-
val show_original_annotation : t -> string
903+
val show_explicit_annotation : t -> string option
896904

897905
(* Show the fully qualified type annotation from the type checker. *)
898906
val show_fully_qualified_annotation : t -> string
899907
end = struct
908+
module ExplicitAnnotation = struct
909+
type t =
910+
| Unsupported
911+
| NotFound
912+
| Found of string
913+
end
914+
900915
type t = {
901-
expression: Expression.t option;
902-
(* The original annotation, as an expression. None if not supported. *)
903-
type_: PyrePysaApi.PysaType.t Lazy.t;
916+
explicit_annotation: ExplicitAnnotation.t Lazy.t;
917+
inferred_type: PyrePysaApi.PysaType.t option Lazy.t;
904918
}
905919

906-
let from_original_annotation ~pyre_api ~preserve_original expression =
920+
let from_pyre1 ~pyre_api = function
921+
| Some expression ->
922+
{
923+
explicit_annotation = lazy (ExplicitAnnotation.Found (Expression.show expression));
924+
inferred_type =
925+
lazy
926+
(Analysis.PyrePysaEnvironment.ReadOnly.parse_annotation pyre_api expression
927+
|> PyrePysaApi.PysaType.from_pyre1_type
928+
|> Option.some);
929+
}
930+
| None -> { explicit_annotation = lazy ExplicitAnnotation.NotFound; inferred_type = lazy None }
931+
932+
933+
let create ~inferred_type ~explicit_annotation =
934+
{ explicit_annotation = lazy explicit_annotation; inferred_type = lazy inferred_type }
935+
936+
937+
let from_inferred_type inferred_type =
907938
{
908-
expression = Option.some_if preserve_original expression;
909-
type_ =
910-
lazy
911-
(PyrePysaApi.ReadOnly.parse_annotation pyre_api expression
912-
|> PyrePysaApi.PysaType.from_pyre1_type);
939+
explicit_annotation = lazy ExplicitAnnotation.Unsupported;
940+
inferred_type = lazy inferred_type;
913941
}
914942

915943

916-
let from_pysa_type type_ = { expression = None; type_ = lazy type_ }
944+
let is_annotated { explicit_annotation; _ } =
945+
match Lazy.force explicit_annotation with
946+
| ExplicitAnnotation.Unsupported -> failwith "is_annotated is not supported in this context"
947+
| ExplicitAnnotation.Found annotation ->
948+
(String.is_prefix ~prefix:"typing.Annotated[" annotation
949+
|| String.is_prefix ~prefix:"Annotated[" annotation)
950+
&& String.is_suffix ~suffix:"]" annotation
951+
| ExplicitAnnotation.NotFound -> false
917952

918-
let is_annotated = function
919-
| { expression = None; _ } -> failwith "is_annotated is not supported in this context"
920-
| { expression = Some { Node.value = expression; _ }; _ } -> (
921-
match expression with
922-
| Expression.Expression.Subscript
923-
{
924-
base =
925-
{ Node.value = Name (Expression.Name.Attribute { attribute = "Annotated"; _ }); _ };
926-
_;
927-
} ->
928-
true
929-
| _ -> false)
930953

954+
let inferred_type { inferred_type; _ } = Lazy.force inferred_type
931955

932-
let as_type { type_; _ } = Lazy.force type_
933-
934-
let as_original_annotation { expression; _ } = expression
956+
let explicit_annotation { explicit_annotation; _ } = Lazy.force explicit_annotation
935957

936958
(* Show the original annotation, as written by the user. *)
937-
let show_original_annotation = function
938-
| { expression = Some expression; _ } -> Expression.show expression
939-
| _ -> failwith "show_original_annotation is not supported in this context"
959+
let show_explicit_annotation { explicit_annotation; _ } =
960+
match Lazy.force explicit_annotation with
961+
| ExplicitAnnotation.Unsupported ->
962+
failwith "show_explicit_annotation is not supported in this context"
963+
| ExplicitAnnotation.Found annotation -> Some annotation
964+
| ExplicitAnnotation.NotFound -> None
940965

941966

942967
(* Show the parsed annotation from pyre *)
943-
let show_fully_qualified_annotation { type_; _ } =
944-
PyrePysaApi.PysaType.show_fully_qualified (Lazy.force type_)
968+
let show_fully_qualified_annotation { inferred_type; _ } =
969+
match Lazy.force inferred_type with
970+
| Some inferred_type -> PyrePysaApi.PysaType.show_fully_qualified inferred_type
971+
| None -> "typing.Any"
945972
end
946973

947974
module Modelable = struct
@@ -957,11 +984,11 @@ module Modelable = struct
957984
}
958985
| Attribute of {
959986
target_name: Reference.t;
960-
type_annotation: TypeAnnotation.t option Lazy.t;
987+
type_annotation: TypeAnnotation.t Lazy.t;
961988
}
962989
| Global of {
963990
target_name: Reference.t;
964-
type_annotation: TypeAnnotation.t option Lazy.t;
991+
type_annotation: TypeAnnotation.t Lazy.t;
965992
}
966993

967994
let create_callable ~pyre_api ~callables_to_definitions_map target =
@@ -1024,14 +1051,25 @@ module Modelable = struct
10241051
~include_generated_attributes:false
10251052
~class_name
10261053
~attribute
1027-
>>| TypeAnnotation.from_original_annotation ~pyre_api ~preserve_original:true
1054+
|> TypeAnnotation.from_pyre1 ~pyre_api:pyre1_api
10281055
| PyrePysaApi.ReadOnly.Pyrefly pyrefly_api ->
1029-
Interprocedural.PyreflyApi.ReadOnly.get_class_attribute_inferred_type
1030-
pyrefly_api
1031-
~class_name
1032-
~attribute
1033-
|> TypeAnnotation.from_pysa_type
1034-
|> Option.some)
1056+
let inferred_type =
1057+
Interprocedural.PyreflyApi.ReadOnly.get_class_attribute_inferred_type
1058+
pyrefly_api
1059+
~class_name
1060+
~attribute
1061+
|> Option.some
1062+
in
1063+
let explicit_annotation =
1064+
Interprocedural.PyreflyApi.ReadOnly.get_class_attribute_explicit_annotation
1065+
pyrefly_api
1066+
~class_name
1067+
~attribute
1068+
|> function
1069+
| Some annotation -> TypeAnnotation.ExplicitAnnotation.Found annotation
1070+
| None -> TypeAnnotation.ExplicitAnnotation.NotFound
1071+
in
1072+
TypeAnnotation.create ~inferred_type ~explicit_annotation)
10351073
in
10361074
Attribute { target_name; type_annotation }
10371075

@@ -1043,15 +1081,19 @@ module Modelable = struct
10431081
(match pyre_api with
10441082
| PyrePysaApi.ReadOnly.Pyre1 pyre1_api ->
10451083
Analysis.PyrePysaEnvironment.ReadOnly.get_global_annotation pyre1_api target_name
1046-
>>| TypeAnnotation.from_original_annotation ~pyre_api ~preserve_original:true
1084+
|> TypeAnnotation.from_pyre1 ~pyre_api:pyre1_api
10471085
| PyrePysaApi.ReadOnly.Pyrefly pyrefly_api ->
10481086
let qualifier = Option.value_exn (Reference.prefix target_name) in
10491087
let name = Reference.last target_name in
1050-
Interprocedural.PyreflyApi.ReadOnly.get_global_inferred_type
1051-
pyrefly_api
1052-
~qualifier
1053-
~name
1054-
>>| TypeAnnotation.from_pysa_type)
1088+
let inferred_type =
1089+
Interprocedural.PyreflyApi.ReadOnly.get_global_inferred_type
1090+
pyrefly_api
1091+
~qualifier
1092+
~name
1093+
in
1094+
TypeAnnotation.create
1095+
~inferred_type
1096+
~explicit_annotation:TypeAnnotation.ExplicitAnnotation.Unsupported)
10551097
in
10561098
Global { target_name; type_annotation }
10571099

source/interprocedural_analyses/taint/modelParseResult.mli

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -445,22 +445,30 @@ end
445445
module TypeAnnotation : sig
446446
type t
447447

448-
val from_original_annotation
449-
: pyre_api:PyrePysaApi.ReadOnly.t ->
450-
preserve_original:bool ->
451-
Ast.Expression.t ->
448+
module ExplicitAnnotation : sig
449+
type t =
450+
| Unsupported
451+
| NotFound
452+
| Found of string
453+
end
454+
455+
val from_pyre1 : pyre_api:Analysis.PyrePysaEnvironment.ReadOnly.t -> Ast.Expression.t option -> t
456+
457+
val create
458+
: inferred_type:PyrePysaApi.PysaType.t option ->
459+
explicit_annotation:ExplicitAnnotation.t ->
452460
t
453461

454-
val from_pysa_type : PyrePysaApi.PysaType.t -> t
462+
val from_inferred_type : PyrePysaApi.PysaType.t option -> t
455463

456464
val is_annotated : t -> bool
457465

458-
val as_type : t -> PyrePysaApi.PysaType.t
466+
val inferred_type : t -> PyrePysaApi.PysaType.t option
459467

460-
val as_original_annotation : t -> Ast.Expression.t option
468+
val explicit_annotation : t -> ExplicitAnnotation.t
461469

462470
(* Show the original annotation, as written by the user. *)
463-
val show_original_annotation : t -> string
471+
val show_explicit_annotation : t -> string option
464472

465473
(* Show the fully qualified type annotation from the type checker. *)
466474
val show_fully_qualified_annotation : t -> string
@@ -483,7 +491,7 @@ module Modelable : sig
483491

484492
val target_name : t -> Ast.Reference.t
485493

486-
val type_annotation : t -> TypeAnnotation.t option
494+
val type_annotation : t -> TypeAnnotation.t
487495

488496
val undecorated_signatures : t -> PyrePysaApi.ModelQueries.FunctionSignature.t list
489497

0 commit comments

Comments
 (0)