Skip to content

Commit

Permalink
[infer/python][2/2] update python integration to allow capture of mul…
Browse files Browse the repository at this point in the history
…tiple files

Summary:
In this diff, we do the minimum update to `TextualSil` and deal with the
fallout so we can run a simple python test in `codetoanalyze/python/pulse`

Reviewed By: artempyanykh

Differential Revision: D46274102

fbshipit-source-id: 1b034c6d78304a8794dc9310a57b6e41477186c1
  • Loading branch information
Vincent Siles authored and facebook-github-bot committed May 31, 2023
1 parent 03f7e27 commit e4fb442
Show file tree
Hide file tree
Showing 31 changed files with 299 additions and 21 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ DIRECT_TESTS += \
endif
endif # BUILD_PLATFORM+BUILD_HACK_ANALYZERS

ifeq ($(BUILD_PLATFORM)+$(BUILD_PYTHON_ANALYZERS)+$(IS_FACEBOOK_TREE),Linux+yes+yes)
DIRECT_TESTS += \
python_pulse \

endif # BUILD_PLATFORM+BUILD_HACK_ANALYZERS

ifeq ($(BUILD_JAVA_ANALYZERS),yes)
BUILD_SYSTEMS_TESTS += \
differential_interesting_paths_filter \
Expand Down
2 changes: 2 additions & 0 deletions infer/src/IR/Fieldname.ml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ let to_simplified_string ({class_name; field_name} : t) =
Some (HackClassName.classname name)
| JavaClass name ->
Some (JavaClassName.classname name)
| PythonClass name ->
Some (PythonClassName.classname name)
in
Option.value_map last_class_name ~default:field_name ~f:(fun last_class_name ->
let sep = match class_name with CppClass _ -> "::" | _ -> "." in
Expand Down
66 changes: 62 additions & 4 deletions infer/src/IR/Procname.ml
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,31 @@ module Hack = struct
let get_class_name_as_a_string {class_name} = Option.map class_name ~f:HackClassName.classname
end

module Python = struct
type t = {class_name: PythonClassName.t option; function_name: string; arity: int option}
[@@deriving compare, equal, yojson_of, sexp, hash]

let get_class_type_name {class_name} = Option.map class_name ~f:(fun cn -> Typ.PythonClass cn)

let pp verbosity fmt t =
let pp_arity verbosity fmt =
match verbosity with
| Verbose -> (
match t.arity with Some arity -> F.fprintf fmt "#%d" arity | None -> () )
| Non_verbose | Simple | NameOnly ->
()
in
match verbosity with
| NameOnly ->
F.fprintf fmt "%s" t.function_name
| Simple | Non_verbose | Verbose -> (
match t.class_name with
| Some class_name ->
F.fprintf fmt "%a.%s%t" PythonClassName.pp class_name t.function_name (pp_arity verbosity)
| _ ->
F.fprintf fmt "%s%t" t.function_name (pp_arity verbosity) )
end

(** Type of procedure names. *)
type t =
| Block of Block.t
Expand All @@ -688,6 +713,7 @@ type t =
| Java of Java.t
| Linters_dummy_method
| ObjC_Cpp of ObjC_Cpp.t
| Python of Python.t
| WithFunctionParameters of t * FunctionParameters.t * FunctionParameters.t list
[@@deriving compare, equal, yojson_of, sexp, hash]

Expand Down Expand Up @@ -780,6 +806,12 @@ let rec compare_name x y =
-1
| _, ObjC_Cpp _ ->
1
| Python name1, Python name2 ->
Python.compare name1 name2
| Python _, _ ->
-1
| _, Python _ ->
1
| WithFunctionParameters (x, _, _), WithFunctionParameters (y, _, _) ->
compare_name x y

Expand Down Expand Up @@ -847,7 +879,7 @@ let rec on_objc_helper ~f ~default = function
f objc_cpp_pname
| WithFunctionParameters (base, _, _) ->
on_objc_helper ~f ~default base
| Block _ | C _ | CSharp _ | Erlang _ | Hack _ | Java _ | Linters_dummy_method ->
| Block _ | C _ | CSharp _ | Erlang _ | Hack _ | Java _ | Linters_dummy_method | Python _ ->
default


Expand Down Expand Up @@ -919,6 +951,8 @@ let rec replace_class t (new_class : Typ.Name.t) =
L.die InternalError "replace_class on ill-formed Hack type"
in
Hack {h with class_name= Some name}
| Python _ ->
L.die InternalError "TODO: replace_class for Python type"
| WithFunctionParameters (base, func, functions) ->
WithFunctionParameters (replace_class base new_class, func, functions)
| C _ | Block _ | Erlang _ | Linters_dummy_method ->
Expand All @@ -937,6 +971,8 @@ let get_class_type_name t =
Block.get_class_type_name block
| Hack hack ->
Hack.get_class_type_name hack
| Python python ->
Python.get_class_type_name python
| C _ | Erlang _ | WithFunctionParameters _ | Linters_dummy_method ->
None

Expand All @@ -953,6 +989,8 @@ let get_class_name t =
Block.get_class_name block
| Hack hack_pname ->
Hack.get_class_name_as_a_string hack_pname
| Python _ ->
L.die InternalError "TODO: get_class_name for Python type"
| C _ | Erlang _ | WithFunctionParameters _ | Linters_dummy_method ->
None

Expand All @@ -967,7 +1005,7 @@ let rec objc_cpp_replace_method_name t (new_method_name : string) =
ObjC_Cpp {osig with method_name= new_method_name}
| WithFunctionParameters (base, func, functions) ->
WithFunctionParameters (objc_cpp_replace_method_name base new_method_name, func, functions)
| C _ | CSharp _ | Block _ | Erlang _ | Hack _ | Linters_dummy_method | Java _ ->
| C _ | CSharp _ | Block _ | Erlang _ | Hack _ | Linters_dummy_method | Java _ | Python _ ->
t


Expand All @@ -992,6 +1030,8 @@ let rec get_method = function
cs.method_name
| Linters_dummy_method ->
"Linters_dummy_method"
| Python name ->
name.function_name


(** Return whether the procname is a block procname. *)
Expand Down Expand Up @@ -1034,6 +1074,8 @@ let rec get_language = function
Language.Java
| CSharp _ ->
Language.CIL
| Python _ ->
Language.Python


(** [is_constructor pname] returns true if [pname] is a constructor *)
Expand Down Expand Up @@ -1072,7 +1114,8 @@ let rec is_static = function
| Erlang _
| Hack _
| Linters_dummy_method
| ObjC_Cpp {kind= CPPMethod _ | CPPConstructor _ | CPPDestructor _} ->
| ObjC_Cpp {kind= CPPMethod _ | CPPConstructor _ | CPPDestructor _}
| Python _ ->
None
| WithFunctionParameters (base, _, _) ->
is_static base
Expand Down Expand Up @@ -1165,6 +1208,8 @@ let rec pp_unique_id fmt = function
pp_with_function_parameters Verbose pp_unique_id fmt base (func :: functions)
| Linters_dummy_method ->
F.pp_print_string fmt "Linters_dummy_method"
| Python h ->
Python.pp Verbose fmt h


let to_unique_id proc_name = F.asprintf "%a" pp_unique_id proc_name
Expand All @@ -1190,6 +1235,8 @@ let rec pp_with_verbosity verbosity fmt = function
(func :: functions)
| Linters_dummy_method ->
pp_unique_id fmt Linters_dummy_method
| Python h ->
Python.pp verbosity fmt h


let pp = pp_with_verbosity Non_verbose
Expand Down Expand Up @@ -1237,6 +1284,8 @@ let rec pp_name_only fmt = function
pp_name_only fmt base
| Linters_dummy_method ->
pp_unique_id fmt Linters_dummy_method
| Python h ->
Python.pp NameOnly fmt h


let patterns_match patterns proc_name =
Expand Down Expand Up @@ -1264,6 +1313,8 @@ let rec pp_simplified_string ?(withclass = false) fmt = function
pp_simplified_string fmt base
| Linters_dummy_method ->
pp_unique_id fmt Linters_dummy_method
| Python h ->
Python.pp Simple fmt h


let to_simplified_string ?withclass proc_name =
Expand Down Expand Up @@ -1338,6 +1389,9 @@ let rec get_parameters procname =
get_parameters base
| Linters_dummy_method ->
[]
| Python _ ->
(* TODO(vsiles) get inspiration from Hack :D *)
[]


let rec replace_parameters new_parameters procname =
Expand Down Expand Up @@ -1406,6 +1460,8 @@ let rec replace_parameters new_parameters procname =
WithFunctionParameters (replace_parameters new_parameters base, func, functions)
| Linters_dummy_method ->
procname
| Python _ ->
procname


let parameter_of_name procname class_name =
Expand Down Expand Up @@ -1443,6 +1499,8 @@ let make_objc_dealloc name = ObjC_Cpp (ObjC_Cpp.make_dealloc name)

let make_objc_copyWithZone ~is_mutable name = ObjC_Cpp (ObjC_Cpp.make_copyWithZone ~is_mutable name)

let make_python ~class_name ~function_name ~arity = Python {class_name; function_name; arity}

let erlang_call_unqualified ~arity = Erlang (Erlang.call_unqualified arity)

let erlang_call_qualified ~arity = Erlang (Erlang.call_qualified arity)
Expand Down Expand Up @@ -1543,7 +1601,7 @@ module Normalizer = HashNormalizer.Make (struct
| Linters_dummy_method | WithFunctionParameters _ ->
(* these kinds should not appear inside a type environment *)
t
| Block _ | CSharp _ | Erlang _ | Hack _ ->
| Block _ | CSharp _ | Erlang _ | Hack _ | Python _ ->
(* TODO *)
t
end)
10 changes: 10 additions & 0 deletions infer/src/IR/Procname.mli
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ module Hack : sig
val get_class_name_as_a_string : t -> string option
end

module Python : sig
(* TODO: revamp this once modules are implemented *)
type t = private {class_name: PythonClassName.t option; function_name: string; arity: int option}
end

(** Type of procedure names. WithFunctionParameters is used for creating an instantiation of a
method that contains non-empty function parameters and it's called with concrete functions. For
example: [foo(Block block) {block();}] [bar() {foo(my_block)}] is executed as
Expand All @@ -252,6 +257,7 @@ type t =
| Java of Java.t
| Linters_dummy_method
| ObjC_Cpp of ObjC_Cpp.t
| Python of Python.t
| WithFunctionParameters of t * FunctionParameters.t * FunctionParameters.t list
[@@deriving compare, yojson_of, sexp, hash]

Expand Down Expand Up @@ -364,6 +370,10 @@ val make_objc_dealloc : Typ.Name.t -> t
val make_objc_copyWithZone : is_mutable:bool -> Typ.Name.t -> t
(** Create an Objective-C method for copyWithZone: or mutableCopyWithZone: according to is_mutable. *)

val make_python :
class_name:PythonClassName.t option -> function_name:string -> arity:int option -> t
(** Create a Python procedure name. *)

val empty_block : t
(** Empty block name. *)

Expand Down
42 changes: 42 additions & 0 deletions infer/src/IR/PythonClassName.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(*
* 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 F = Format

(* TODO: add support for
- module names
- nested classes
*)
type t = {classname: string} [@@deriving compare, equal, yojson_of, sexp, hash]

let make classname = {classname}

let classname {classname} = classname

let components {classname} = [classname]

let wildcard = make "?"

let pp fmt {classname} = F.fprintf fmt "%s" classname

let to_string = Pp.string_of_pp pp

let static_suffix = "$static"

let len_static_suffix = String.length static_suffix

let static_companion {classname} = {classname= classname ^ static_suffix}

let is_static {classname} = StringLabels.ends_with ~suffix:static_suffix classname

let static_companion_origin ({classname} as name) =
let len_classname = String.length classname in
if len_classname > len_static_suffix then
let len = len_classname - len_static_suffix in
{classname= StringLabels.sub ~pos:0 ~len classname}
else name
36 changes: 36 additions & 0 deletions infer/src/IR/PythonClassName.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
(*
* 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 F = Format

type t [@@deriving compare, equal, yojson_of, sexp, hash]

val make : string -> t

val classname : t -> string

val components : t -> string list

val wildcard : t [@@warning "-unused-value-declaration"]

val pp : F.formatter -> t -> unit

val to_string : t -> string

val static_companion : t -> t
[@@warning "-unused-value-declaration"]
(** return the class of the companion class object of this class eg: Foo -> Foo$static *)

val static_companion_origin : t -> t
[@@warning "-unused-value-declaration"]
(** return the origin class of a companion class object eg: Foo$static -> Foo. the result is not
specified if is the name is not a valid static class name *)

val is_static : t -> bool
[@@warning "-unused-value-declaration"]
(** tests if the name is a valid static class name (ie. ends with "$static") *)
2 changes: 2 additions & 0 deletions infer/src/IR/Struct.ml
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ let merge typename ~newer ~current =
Group 2 are valid cases. There's not much benefit in differentiating between all these
cases, hence when we see two non-dummy Hack structs we just do a full merge. *)
full_merge ~newer ~current
| PythonClass _ ->
Logging.die InternalError "TODO: IR.Struct.merge for Python"


let is_not_java_interface = function
Expand Down
2 changes: 2 additions & 0 deletions infer/src/IR/Tenv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ let resolve_method ~method_exists tenv class_name proc_name =
class_struct.supers
| ObjcProtocol _ ->
[]
| PythonClass _ ->
L.die InternalError "TODO: inheritance for Python"
in
List.find_map supers_to_search ~f:resolve_name )
and resolve_name class_name = lookup tenv class_name >>= resolve_name_struct class_name in
Expand Down
Loading

0 comments on commit e4fb442

Please sign in to comment.