[erl-frontend] Activate Pulse for Erlang

Summary:
After activation, a few bugs manifested and are fixed in this commit:

1. Create an (empty) Tenv per file. Previously, each file said there
will be a global Tenv, but no such global Tenv was ever saved. (There is
no static typing at all at the moment in the Erlang frontend.)

2. Procedures are now marked as defined. If the is_defined attribute is
false, then the analysis will not run even if the flowgraph in Procdesc
is nontrivial.

3. Non-dummy Start/Exit nodes for Procdesc. When a Procdesc is created,
it gets dummy Start/Exit nodes. These dummy nodes have a dummy location,
which causes debug output to have wrong directories/links.

4. Filenames from Erlang function names. By default, the verbose
procedure name was used to make file names for the debug output. But,
Erlang function names contain '/', which doesn't play well with
filenames. That's now replaced by '#' for the purposes of constructing
filenames.

Reviewed By: jvillard

Differential Revision: D29166049

fbshipit-source-id: 48346a05b
master
Radu Grigore 3 years ago committed by Facebook GitHub Bot
parent a4a9433982
commit 2d1ccf5994

@ -31,7 +31,6 @@ let get_all_defined_proc_names cfg =
!procs !procs
(** Create a new procdesc *)
let create_proc_desc cfg (proc_attributes : ProcAttributes.t) = let create_proc_desc cfg (proc_attributes : ProcAttributes.t) =
let pdesc = Procdesc.from_proc_attributes proc_attributes in let pdesc = Procdesc.from_proc_attributes proc_attributes in
let pname = proc_attributes.proc_name in let pname = proc_attributes.proc_name in

@ -28,7 +28,8 @@ val create : unit -> t
(** create a new empty cfg *) (** create a new empty cfg *)
val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t
(** Create a new procdesc and add it to the cfg *) (** Create a new procdesc. If the procedure is defined, you need to create and set the start/exit
nodes after creating the procedure. *)
val iter_sorted : t -> f:(Procdesc.t -> unit) -> unit val iter_sorted : t -> f:(Procdesc.t -> unit) -> unit
(** Iterate over all the proc descs in the cfg in ascending order *) (** Iterate over all the proc descs in the cfg in ascending order *)

@ -11,11 +11,14 @@ open! IStd
(** Module to handle IO. Includes html and xml modules. *) (** Module to handle IO. Includes html and xml modules. *)
module F = Format module F = Format
module L = Logging
(* =============== START of module Html =============== *) (* =============== START of module Html =============== *)
module Html = struct module Html = struct
(** Create a new html file *) (** Create a new html file *)
let create source path = let create source path =
if SourceFile.is_invalid source then
L.debug Capture Verbose "Invalid source. (Did you forget to create a start/exit node?)" ;
let fname, dir_path = let fname, dir_path =
match List.rev path with match List.rev path with
| fname :: path_rev -> | fname :: path_rev ->

@ -260,6 +260,7 @@ val get_locals : t -> ProcAttributes.var_data list
(** Return name and type and attributes of local variables *) (** Return name and type and attributes of local variables *)
val get_nodes : t -> Node.t list val get_nodes : t -> Node.t list
(** Return the nodes, excluding the start node and the exit node. *)
val get_proc_name : t -> Procname.t val get_proc_name : t -> Procname.t

@ -467,14 +467,18 @@ end
module Erlang = struct module Erlang = struct
type t = {module_name: string; function_name: string; arity: int} [@@deriving compare, yojson_of] type t = {module_name: string; function_name: string; arity: int} [@@deriving compare, yojson_of]
let pp verbosity fmt {module_name; function_name; arity} = let pp_general arity_sep verbosity fmt {module_name; function_name; arity} =
match verbosity with match verbosity with
| Simple | Non_verbose -> | Simple | Non_verbose ->
F.fprintf fmt "%s/%d" function_name arity F.fprintf fmt "%s%c%d" function_name arity_sep arity
| Verbose -> | Verbose ->
F.fprintf fmt "%s:%s/%d" module_name function_name arity F.fprintf fmt "%s:%s%c%d" module_name function_name arity_sep arity
let pp verbosity fmt pname = pp_general '/' verbosity fmt pname
let pp_filename fmt pname = pp_general '#' Verbose fmt pname
let set_arity arity name = {name with arity} let set_arity arity name = {name with arity}
end end
@ -1084,6 +1088,8 @@ let to_filename pname =
let pp_mangled fmt = function None -> () | Some mangled -> F.fprintf fmt "#%s" mangled in let pp_mangled fmt = function None -> () | Some mangled -> F.fprintf fmt "#%s" mangled in
F.asprintf "%a%a%a" pp_rev_qualified pname Parameter.pp_parameters parameters pp_mangled F.asprintf "%a%a%a" pp_rev_qualified pname Parameter.pp_parameters parameters pp_mangled
mangled mangled
| Erlang pname ->
F.asprintf "%a" Erlang.pp_filename pname
| ObjC_Cpp objc_cpp -> | ObjC_Cpp objc_cpp ->
F.asprintf "%a%a#%a" pp_rev_qualified pname Parameter.pp_parameters objc_cpp.parameters F.asprintf "%a%a#%a" pp_rev_qualified pname Parameter.pp_parameters objc_cpp.parameters
ObjC_Cpp.pp_verbose_kind objc_cpp.kind ObjC_Cpp.pp_verbose_kind objc_cpp.kind

@ -155,7 +155,7 @@ let all_checkers =
; { checker= Pulse ; { checker= Pulse
; callbacks= ; callbacks=
(let pulse = interprocedural Payloads.Fields.pulse Pulse.checker in (let pulse = interprocedural Payloads.Fields.pulse Pulse.checker in
[(pulse, Clang); (pulse, Java)] ) } [(pulse, Clang); (pulse, Erlang); (pulse, Java)] ) }
; { checker= Impurity ; { checker= Impurity
; callbacks= ; callbacks=
(let impurity = (let impurity =

@ -388,9 +388,16 @@ let translate_one_function env cfg function_ clauses =
let default = ProcAttributes.default env.location.file name in let default = ProcAttributes.default env.location.file name in
let access : ProcAttributes.access = if Set.mem env.exports uf_name then Public else Private in let access : ProcAttributes.access = if Set.mem env.exports uf_name then Public else Private in
let formals = List.init ~f:(fun i -> (mangled_arg i, any)) arity in let formals = List.init ~f:(fun i -> (mangled_arg i, any)) arity in
{default with access; formals; loc= env.location; ret_type= any} {default with access; formals; is_defined= true; loc= env.location; ret_type= any}
in in
let procdesc =
let procdesc = Cfg.create_proc_desc cfg attributes in let procdesc = Cfg.create_proc_desc cfg attributes in
let start_node = Procdesc.create_node procdesc env.location Start_node [] in
let exit_node = Procdesc.create_node procdesc env.location Exit_node [] in
Procdesc.set_start_node procdesc start_node ;
Procdesc.set_exit_node procdesc exit_node ;
procdesc
in
let env = {env with procdesc= Some procdesc; result= Some (Exp.Lvar (Pvar.get_ret_pvar name))} in let env = {env with procdesc= Some procdesc; result= Some (Exp.Lvar (Pvar.get_ret_pvar name))} in
let idents, loads = let idents, loads =
let load (formal, typ) = let load (formal, typ) =
@ -436,8 +443,8 @@ let translate_functions env cfg module_ =
in in
List.iter module_ ~f ; List.iter module_ ~f ;
DB.Results_dir.init env.location.file ; DB.Results_dir.init env.location.file ;
Cfg.store env.location.file cfg ; let tenv = Tenv.FileLocal (Tenv.create ()) in
SourceFiles.add env.location.file cfg Tenv.Global None SourceFiles.add env.location.file cfg tenv None
let translate_module module_ = let translate_module module_ =

Loading…
Cancel
Save