Skip to content

Commit

Permalink
[inferpython][specialization type] using new import_from type in Puls…
Browse files Browse the repository at this point in the history
…e models

Summary: now we can use this new type to resolve more closures without specialization when the closure is associated with an `from .. import .. as ..` statement.

Reviewed By: geralt-encore

Differential Revision:
D67140811

Privacy Context Container: L1208441

fbshipit-source-id: e11f2bbd91b51ded1f3d22e11a4808197cbc6812
  • Loading branch information
davidpichardie authored and facebook-github-bot committed Dec 13, 2024
1 parent 1bcba18 commit a632165
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 23 deletions.
15 changes: 15 additions & 0 deletions infer/src/IR/PythonClassName.ml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,18 @@ let is_module {classname} = String.is_prefix classname ~prefix:globals_prefix
let get_module_name {classname} = String.chop_prefix classname ~prefix:globals_prefix

let is_final name = is_module name

let module_attribute_prefix = "PyModuleAttr::"

let is_module_attribute {classname} = String.is_prefix classname ~prefix:module_attribute_prefix

let get_module_attribute_infos {classname} =
let open IOption.Let_syntax in
let* last_pos = String.substr_index_all classname ~may_overlap:false ~pattern:"::" |> List.last in
let length = String.length classname in
let attribute_name = String.sub classname ~pos:(last_pos + 2) ~len:(length - last_pos - 2) in
let+ module_name =
String.sub classname ~pos:0 ~len:last_pos |> String.chop_prefix ~prefix:module_attribute_prefix
in
let classname = globals_prefix ^ module_name in
({classname}, attribute_name)
6 changes: 6 additions & 0 deletions infer/src/IR/PythonClassName.mli
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ val is_final : t -> bool

val is_module : t -> bool

val is_module_attribute : t -> bool

val get_module_attribute_infos : t -> (t * string) option
(** will return the pair (module_name, attribute) params of the type iff the type name is a module
attribute type *)

val get_module_name : t -> string option
(** will return the string representation of the module iff type name is a module type *)

Expand Down
13 changes: 13 additions & 0 deletions infer/src/IR/Typ.ml
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,23 @@ module Name = struct
match name with PythonClass py -> PythonClassName.is_module py | _ -> false


let is_python_module_attribute name =
match name with PythonClass py -> PythonClassName.is_module_attribute py | _ -> false


let get_python_module_name name =
match name with PythonClass py -> PythonClassName.get_module_name py | _ -> None


let get_python_module_attribute_infos name =
match name with
| PythonClass py ->
PythonClassName.get_module_attribute_infos py
|> Option.map ~f:(fun (py_name, str) -> (PythonClass py_name, str))
| _ ->
None


let is_same_type t1 t2 =
match (t1, t2) with
| CStruct _, CStruct _
Expand Down
4 changes: 4 additions & 0 deletions infer/src/IR/Typ.mli
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,12 @@ module Name : sig

val is_python_module : t -> bool

val is_python_module_attribute : t -> bool

val get_python_module_name : t -> string option

val get_python_module_attribute_infos : t -> (t * string) option

module C : sig
val from_string : string -> t

Expand Down
26 changes: 19 additions & 7 deletions infer/src/pulse/PulseModelsPython.ml
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,26 @@ module Dict = struct

let propagate_static_type_on_load dict key load_res : unit DSL.model_monad =
let open DSL.Syntax in
let rec propagate_field_type tname key =
let field = Fieldname.make tname key in
let* opt_info = tenv_resolve_field_info tname field in
option_iter opt_info ~f:(fun {Struct.typ= field_typ} ->
let opt_static_tname = Typ.name (Typ.strip_ptr field_typ) in
option_iter opt_static_tname ~f:(fun static_tname ->
if Typ.Name.is_python_module_attribute static_tname then
let tname, attr =
Typ.Name.get_python_module_attribute_infos static_tname |> Option.value_exn
in
propagate_field_type tname attr
else add_static_type static_tname load_res ) )
in
let* opt_key = as_constant_string key in
let key =
Option.value_or_thunk opt_key ~default:(fun () ->
L.die InternalError "expecting constant string value" )
in
let* opt_static_type = get_static_type dict in
option_iter opt_static_type ~f:(fun tname ->
let* field = sil_fieldname_from_string_value_exn tname key in
let* opt_info = tenv_resolve_field_info tname field in
option_iter opt_info ~f:(fun {Struct.typ= field_typ} ->
let opt_static_tname = Typ.name (Typ.strip_ptr field_typ) in
option_iter opt_static_tname ~f:(fun static_tname ->
add_static_type static_tname load_res ) ) )
option_iter opt_static_type ~f:(fun tname -> propagate_field_type tname key)


let get dict key : DSL.aval DSL.model_monad =
Expand Down
4 changes: 4 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# 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.
4 changes: 0 additions & 4 deletions infer/tests/codetoanalyze/python/pulse/async_import_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@ async def with_from_import_bad():

async def with_from_import_ok():
await async_await(asyncio.sleep(1))

#TODO: remove the need for explicit call here
asyncio.run(with_from_import_ok())
asyncio.run(with_from_import_bad())
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,47 @@

async def bad1():
await dont_await_it1(asyncio.sleep(1))
asyncio.run(bad1())


async def ok1():
await await_it1(asyncio.sleep(1))
asyncio.run(ok1())


async def bad2():
await dont_await_it2(asyncio.sleep(1))
asyncio.run(bad2())


async def ok2():
await await_it2(asyncio.sleep(1))
asyncio.run(ok2())


async def bad3():
await dont_await_it3(asyncio.sleep(1))
asyncio.run(bad3())


async def ok3():
await await_it3(asyncio.sleep(1))
asyncio.run(ok3())


async def bad4():
await dont_await_it4(asyncio.sleep(1))
asyncio.run(bad4())


async def ok4():
await await_it4(asyncio.sleep(1))
asyncio.run(ok4())


async def bad5():
await dont_await_it5(asyncio.sleep(1))
asyncio.run(bad5())


async def ok5():
await await_it5(asyncio.sleep(1))
asyncio.run(ok5())


async def bad6():
await dont_await_it6(asyncio.sleep(1))
asyncio.run(bad6())


async def ok6():
await await_it6(asyncio.sleep(1))
asyncio.run(ok6())

0 comments on commit a632165

Please sign in to comment.