From 2d1ccf59945049d3eef02f227d364df1fa6ac592 Mon Sep 17 00:00:00 2001 From: Radu Grigore Date: Thu, 24 Jun 2021 03:10:24 -0700 Subject: [PATCH] [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 --- infer/src/IR/Cfg.ml | 1 - infer/src/IR/Cfg.mli | 3 ++- infer/src/IR/Io_infer.ml | 3 +++ infer/src/IR/Procdesc.mli | 1 + infer/src/IR/Procname.ml | 12 +++++++++--- infer/src/backend/registerCheckers.ml | 2 +- infer/src/erlang/ErlangTranslator.ml | 15 +++++++++++---- 7 files changed, 27 insertions(+), 10 deletions(-) diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index 708f2549f..ca6c80455 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -31,7 +31,6 @@ let get_all_defined_proc_names cfg = !procs -(** Create a new procdesc *) let create_proc_desc cfg (proc_attributes : ProcAttributes.t) = let pdesc = Procdesc.from_proc_attributes proc_attributes in let pname = proc_attributes.proc_name in diff --git a/infer/src/IR/Cfg.mli b/infer/src/IR/Cfg.mli index 95a88e71f..5f0aa2534 100644 --- a/infer/src/IR/Cfg.mli +++ b/infer/src/IR/Cfg.mli @@ -28,7 +28,8 @@ val create : unit -> t (** create a new empty cfg *) 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 (** Iterate over all the proc descs in the cfg in ascending order *) diff --git a/infer/src/IR/Io_infer.ml b/infer/src/IR/Io_infer.ml index 7377fe1b4..a2135adb0 100644 --- a/infer/src/IR/Io_infer.ml +++ b/infer/src/IR/Io_infer.ml @@ -11,11 +11,14 @@ open! IStd (** Module to handle IO. Includes html and xml modules. *) module F = Format +module L = Logging (* =============== START of module Html =============== *) module Html = struct (** Create a new html file *) 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 = match List.rev path with | fname :: path_rev -> diff --git a/infer/src/IR/Procdesc.mli b/infer/src/IR/Procdesc.mli index 0575165bb..475e6fa75 100644 --- a/infer/src/IR/Procdesc.mli +++ b/infer/src/IR/Procdesc.mli @@ -260,6 +260,7 @@ val get_locals : t -> ProcAttributes.var_data list (** Return name and type and attributes of local variables *) 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 diff --git a/infer/src/IR/Procname.ml b/infer/src/IR/Procname.ml index bdda4d265..7909bc5c2 100644 --- a/infer/src/IR/Procname.ml +++ b/infer/src/IR/Procname.ml @@ -467,14 +467,18 @@ end module Erlang = struct 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 | Simple | Non_verbose -> - F.fprintf fmt "%s/%d" function_name arity + F.fprintf fmt "%s%c%d" function_name arity_sep arity | 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} end @@ -1084,6 +1088,8 @@ let to_filename pname = 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 mangled + | Erlang pname -> + F.asprintf "%a" Erlang.pp_filename pname | ObjC_Cpp objc_cpp -> F.asprintf "%a%a#%a" pp_rev_qualified pname Parameter.pp_parameters objc_cpp.parameters ObjC_Cpp.pp_verbose_kind objc_cpp.kind diff --git a/infer/src/backend/registerCheckers.ml b/infer/src/backend/registerCheckers.ml index 167853467..3a06a4023 100644 --- a/infer/src/backend/registerCheckers.ml +++ b/infer/src/backend/registerCheckers.ml @@ -155,7 +155,7 @@ let all_checkers = ; { checker= Pulse ; callbacks= (let pulse = interprocedural Payloads.Fields.pulse Pulse.checker in - [(pulse, Clang); (pulse, Java)] ) } + [(pulse, Clang); (pulse, Erlang); (pulse, Java)] ) } ; { checker= Impurity ; callbacks= (let impurity = diff --git a/infer/src/erlang/ErlangTranslator.ml b/infer/src/erlang/ErlangTranslator.ml index b63abe5f8..32ae0c836 100644 --- a/infer/src/erlang/ErlangTranslator.ml +++ b/infer/src/erlang/ErlangTranslator.ml @@ -388,9 +388,16 @@ let translate_one_function env cfg function_ clauses = 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 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 + let procdesc = + 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 procdesc = Cfg.create_proc_desc cfg attributes in let env = {env with procdesc= Some procdesc; result= Some (Exp.Lvar (Pvar.get_ret_pvar name))} in let idents, loads = let load (formal, typ) = @@ -436,8 +443,8 @@ let translate_functions env cfg module_ = in List.iter module_ ~f ; DB.Results_dir.init env.location.file ; - Cfg.store env.location.file cfg ; - SourceFiles.add env.location.file cfg Tenv.Global None + let tenv = Tenv.FileLocal (Tenv.create ()) in + SourceFiles.add env.location.file cfg tenv None let translate_module module_ =