[specs] put specs files operations in their own module

Summary:
Summary.ml defines both a bunch of types and how to use them and a
mechanism to save and store summaries on disk while maintaining a
complex in-memory cache of what's on disk. Make the distinction clear.

Reviewed By: ngorogiannis

Differential Revision: D16358869

fbshipit-source-id: 9d4c6cb77
master
Jules Villard 5 years ago committed by Facebook Github Bot
parent 124ab9fed7
commit 73179f7182

@ -13,7 +13,7 @@ open! IStd
let new_session node =
let pname = Procdesc.Node.get_proc_name node in
let node_id = (Procdesc.Node.get_id node :> int) in
match Summary.get pname with
match Summary.OnDisk.get pname with
| None ->
0
| Some summary ->

@ -13,7 +13,7 @@ module L = Logging
let clear_caches () =
Ondemand.clear_cache () ;
Summary.clear_cache () ;
Summary.OnDisk.clear_cache () ;
Typ.Procname.SQLite.clear_cache ()
@ -91,7 +91,7 @@ let main ~changed_files =
register_active_checkers () ;
if Config.reanalyze then (
L.progress "Invalidating procedures to be reanalyzed@." ;
Summary.reset_all ~filter:(Lazy.force Filtering.procedures_filter) () ;
Summary.OnDisk.reset_all ~filter:(Lazy.force Filtering.procedures_filter) () ;
L.progress "Done@." )
else DB.Results_dir.clean_specs_dir () ;
let n_all_source_files = ref 0 in

@ -50,7 +50,7 @@ let spec_files_from_cmdline () =
let summary_iterator spec_files =
let sorted_spec_files = List.sort ~compare:String.compare (spec_files ()) in
let do_spec f fname =
match Summary.load_from_file (DB.filename_from_string fname) with
match Summary.OnDisk.load_from_file (DB.filename_from_string fname) with
| None ->
L.(die UserError) "Error: cannot open file %s@." fname
| Some summary ->
@ -65,5 +65,7 @@ let iter_from_config ~f = summary_iterator spec_files_from_cmdline f
let iter ~f = summary_iterator load_specfiles f
let delete pname =
let filename = Summary.specs_filename_of_procname pname |> DB.filename_to_string in
Unix.unlink filename ; Ondemand.remove_from_cache pname ; Summary.remove_from_cache pname
let filename = Summary.OnDisk.specs_filename_of_procname pname |> DB.filename_to_string in
Unix.unlink filename ;
Ondemand.remove_from_cache pname ;
Summary.OnDisk.remove_from_cache pname

@ -8,7 +8,6 @@
open! IStd
module F = Format
open PolyVariantEqual
module Stats = struct
type t =
@ -96,14 +95,6 @@ let get_err_log summary = summary.err_log
let get_loc summary = (get_attributes summary).ProcAttributes.loc
type cache = t Typ.Procname.Hash.t
let cache : cache = Typ.Procname.Hash.create 128
let clear_cache () = Typ.Procname.Hash.clear cache
let remove_from_cache pname = Typ.Procname.Hash.remove cache pname
let pp_errlog fmt err_log =
F.fprintf fmt "ERRORS: @[<h>%a@]@\n%!" Errlog.pp_errors err_log ;
F.fprintf fmt "WARNINGS: @[<h>%a@]" Errlog.pp_warnings err_log
@ -139,43 +130,56 @@ let pp_html source fmt summary =
F.fprintf fmt "</LISTING>@\n"
(** Add the summary to the table for the given function *)
let add (proc_name : Typ.Procname.t) (summary : t) : unit =
module OnDisk = struct
open PolyVariantEqual
type cache = t Typ.Procname.Hash.t
let cache : cache = Typ.Procname.Hash.create 128
let clear_cache () = Typ.Procname.Hash.clear cache
let remove_from_cache pname = Typ.Procname.Hash.remove cache pname
(** Add the summary to the table for the given function *)
let add (proc_name : Typ.Procname.t) (summary : t) : unit =
Typ.Procname.Hash.replace cache proc_name summary
let specs_filename pname =
let specs_filename pname =
let pname_file = Typ.Procname.to_filename pname in
pname_file ^ Config.specs_files_suffix
(** Return the path to the .specs file for the given procedure in the current results directory *)
let specs_filename_of_procname pname =
(** Return the path to the .specs file for the given procedure in the current results directory *)
let specs_filename_of_procname pname =
DB.Results_dir.path_to_filename DB.Results_dir.Abs_root
[Config.specs_dir_name; specs_filename pname]
(** paths to the .specs file for the given procedure in the current spec libraries *)
let specs_library_filename specs_dir pname =
(** paths to the .specs file for the given procedure in the current spec libraries *)
let specs_library_filename specs_dir pname =
DB.filename_from_string (Filename.concat specs_dir (specs_filename pname))
(** paths to the .specs file for the given procedure in the models folder *)
let specs_models_filename pname =
(** paths to the .specs file for the given procedure in the models folder *)
let specs_models_filename pname =
DB.filename_from_string (Filename.concat Config.models_dir (specs_filename pname))
let has_model pname = Sys.file_exists (DB.filename_to_string (specs_models_filename pname)) = `Yes
let has_model pname =
Sys.file_exists (DB.filename_to_string (specs_models_filename pname)) = `Yes
let summary_serializer : t Serialization.serializer =
let summary_serializer : t Serialization.serializer =
Serialization.create_serializer Serialization.Key.summary
(** Load procedure summary from the given file *)
let load_from_file specs_file = Serialization.read_from_file summary_serializer specs_file
(** Load procedure summary from the given file *)
let load_from_file specs_file = Serialization.read_from_file summary_serializer specs_file
(** Load procedure summary for the given procedure name and update spec table *)
let load_summary_to_spec_table =
(** Load procedure summary for the given procedure name and update spec table *)
let load_summary_to_spec_table =
let rec or_load_summary_libs specs_dirs proc_name summ_opt =
match (summ_opt, specs_dirs) with
| Some _, _ | _, [] ->
@ -202,25 +206,28 @@ let load_summary_to_spec_table =
summ_opt
let get proc_name =
try Some (Typ.Procname.Hash.find cache proc_name)
with Caml.Not_found -> load_summary_to_spec_table proc_name
let get proc_name =
match Typ.Procname.Hash.find cache proc_name with
| summary ->
Some summary
| exception Caml.Not_found ->
load_summary_to_spec_table proc_name
(** Check if the procedure is from a library:
(** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *)
let proc_is_library proc_attributes =
let proc_is_library proc_attributes =
if not proc_attributes.ProcAttributes.is_defined then
match get proc_attributes.ProcAttributes.proc_name with None -> true | Some _ -> false
else false
(** Try to find the attributes for a defined proc.
(** Try to find the attributes for a defined proc.
First look at specs (to get attributes computed by analysis)
then look at the attributes table.
If no attributes can be found, return None.
*)
let proc_resolve_attributes proc_name =
let proc_resolve_attributes proc_name =
match get proc_name with
| Some summary ->
Some (get_attributes summary)
@ -228,8 +235,8 @@ let proc_resolve_attributes proc_name =
Attributes.load proc_name
(** Save summary for the procedure into the spec database *)
let store (summ : t) =
(** Save summary for the procedure into the spec database *)
let store (summ : t) =
let final_summary = {summ with status= Status.Analyzed} in
let proc_name = get_proc_name final_summary in
(* Make sure the summary in memory is identical to the saved one *)
@ -239,7 +246,7 @@ let store (summ : t) =
~data:final_summary
let init_summary proc_desc =
let reset proc_desc =
let summary =
{ sessions= ref 0
; payloads= Payloads.empty
@ -253,18 +260,15 @@ let init_summary proc_desc =
summary
let dummy =
let dummy =
let dummy_attributes =
ProcAttributes.default (SourceFile.invalid __FILE__) Typ.Procname.empty_block
in
let dummy_proc_desc = Procdesc.from_proc_attributes dummy_attributes in
init_summary dummy_proc_desc
reset dummy_proc_desc
(** Reset a summary rebuilding the dependents and preserving the proc attributes if present. *)
let reset proc_desc = init_summary proc_desc
let reset_all ~filter () =
let reset_all ~filter () =
let reset proc_name =
let filename = specs_filename_of_procname proc_name in
Serialization.read_from_file summary_serializer filename
@ -273,7 +277,7 @@ let reset_all ~filter () =
Serialization.write_to_file summary_serializer filename ~data:blank_summary )
in
Procedures.get_all ~filter () |> List.iter ~f:reset
end
module SummaryValue = struct
type nonrec t = t option

@ -51,22 +51,6 @@ type t =
val poly_fields : t PolyFields.t
val dummy : t
(** dummy summary for testing *)
val has_model : Typ.Procname.t -> bool
(** Check if a summary for a given procedure exists in the models directory *)
val clear_cache : unit -> unit
(** Remove all the elements from the cache of summaries *)
val remove_from_cache : Typ.Procname.t -> unit
(** Remove an element from the cache of summaries. Contrast to reset which re-initializes a summary
keeping the same Procdesc and updates the cache accordingly. *)
val get : Typ.Procname.t -> t option
(** Return the summary option for the procedure name *)
val get_proc_name : t -> Typ.Procname.t
(** Get the procedure name *)
@ -88,36 +72,54 @@ val get_signature : t -> string
val get_status : t -> Status.t
(** Return the status (active v.s. inactive) of a procedure summary *)
val reset : Procdesc.t -> t
(** Reset a summary rebuilding the dependents and preserving the proc attributes if present. *)
val specs_filename_of_procname : Typ.Procname.t -> DB.filename
(** Return the path to the .specs file for the given procedure in the current results directory *)
val load_from_file : DB.filename -> t option
(** Load procedure summary from the given file *)
val pp_html : SourceFile.t -> Format.formatter -> t -> unit
(** Print the summary in html format *)
val pp_text : Format.formatter -> t -> unit
(** Print the summary in text format *)
val proc_resolve_attributes : Typ.Procname.t -> ProcAttributes.t option
(** Try to find the attributes for a defined proc.
module OnDisk : sig
val has_model : Typ.Procname.t -> bool
(** Check if a summary for a given procedure exists in the models directory *)
val clear_cache : unit -> unit
(** Remove all the elements from the cache of summaries *)
val remove_from_cache : Typ.Procname.t -> unit
(** Remove an element from the cache of summaries. Contrast to reset which re-initializes a summary
keeping the same Procdesc and updates the cache accordingly. *)
val get : Typ.Procname.t -> t option
(** Return the summary option for the procedure name *)
val reset : Procdesc.t -> t
(** Reset a summary rebuilding the dependents and preserving the proc attributes if present. *)
val specs_filename_of_procname : Typ.Procname.t -> DB.filename
(** Return the path to the .specs file for the given procedure in the current results directory *)
val load_from_file : DB.filename -> t option
(** Load procedure summary from the given file *)
val proc_resolve_attributes : Typ.Procname.t -> ProcAttributes.t option
(** Try to find the attributes for a defined proc.
First look at specs (to get attributes computed by analysis)
then look at the attributes table.
If no attributes can be found, return None.
*)
*)
val proc_is_library : ProcAttributes.t -> bool
(** Check if the procedure is from a library:
val proc_is_library : ProcAttributes.t -> bool
(** Check if the procedure is from a library:
It's not defined, and there is no spec file for it. *)
val store : t -> unit
(** Save summary for the procedure into the spec database *)
val store : t -> unit
(** Save summary for the procedure into the spec database *)
val reset_all : filter:Filtering.procedures_filter -> unit -> unit
val reset_all : filter:Filtering.procedures_filter -> unit -> unit
val dummy : t
(** dummy summary for testing *)
end
module SummaryValue : Memcached.Value with type t = t option

@ -42,7 +42,7 @@ let get_procedure_definition exe_env proc_name =
Procdesc.load proc_name
|> Option.map ~f:(fun proc_desc ->
let tenv = Exe_env.get_tenv exe_env proc_name in
(tenv, Summary.reset proc_desc) )
(tenv, Summary.OnDisk.reset proc_desc) )
(** Invoke all registered procedure callbacks on the given procedure. *)

@ -41,7 +41,7 @@ let is_active, add_active, remove_active =
let already_analyzed proc_name =
match Summary.get proc_name with
match Summary.OnDisk.get proc_name with
| Some summary ->
Summary.(Status.is_analyzed (get_status summary))
| None ->
@ -57,7 +57,7 @@ let should_be_analyzed proc_attributes =
let get_proc_attr proc_name =
IList.force_until_first_some
[lazy (Summary.proc_resolve_attributes proc_name); lazy (Topl.get_proc_attr proc_name)]
[lazy (Summary.OnDisk.proc_resolve_attributes proc_name); lazy (Topl.get_proc_attr proc_name)]
let procedure_should_be_analyzed proc_name =
@ -158,12 +158,12 @@ let run_proc_analysis ~caller_pdesc callee_pdesc =
Typ.Procname.pp callee_pname ;
let preprocess () =
incr nesting ;
let initial_callee_summary = Summary.reset callee_pdesc in
let initial_callee_summary = Summary.OnDisk.reset callee_pdesc in
add_active callee_pname ; initial_callee_summary
in
let postprocess summary =
decr nesting ;
Summary.store summary ;
Summary.OnDisk.store summary ;
remove_active callee_pname ;
Printer.write_proc_html callee_pdesc ;
log_elapsed_time () ;
@ -180,7 +180,10 @@ let run_proc_analysis ~caller_pdesc callee_pdesc =
{summary.payloads with biabduction}
in
let new_summary = {summary with stats; payloads} in
Summary.store new_summary ; remove_active callee_pname ; log_elapsed_time () ; new_summary
Summary.OnDisk.store new_summary ;
remove_active callee_pname ;
log_elapsed_time () ;
new_summary
in
let old_state = save_global_state () in
let initial_callee_summary = preprocess () in
@ -293,7 +296,7 @@ let register_callee ?caller_summary callee_pname =
let get_proc_desc callee_pname =
IList.force_until_first_some
[ lazy (Procdesc.load callee_pname)
; lazy (Option.map ~f:Summary.get_proc_desc (Summary.get callee_pname))
; lazy (Option.map ~f:Summary.get_proc_desc (Summary.OnDisk.get callee_pname))
; lazy (Topl.get_proc_desc callee_pname) ]
@ -318,10 +321,10 @@ let analyze_proc ?caller_summary callee_pname callee_pdesc should_be_analyzed =
callee_pdesc)
, true )
| None ->
(Summary.get callee_pname, true)
(Summary.OnDisk.get callee_pname, true)
else (
EventLogger.log_skipped_pname (F.asprintf "%a" Typ.Procname.pp callee_pname) ;
(Summary.get callee_pname, true) )
(Summary.OnDisk.get callee_pname, true) )
in
if update_memcached then memcache_set callee_pname callee_summary_option ;
Typ.Procname.Hash.add cache callee_pname callee_summary_option ;

@ -209,7 +209,7 @@ let do_funptr_sub summary tenv =
let do_preanalysis pdesc tenv =
let summary = Summary.reset pdesc in
let summary = Summary.OnDisk.reset pdesc in
if
Config.function_pointer_specialization
&& not (Typ.Procname.is_java (Procdesc.get_proc_name pdesc))

@ -69,7 +69,7 @@ let curr_html_formatter = ref F.std_formatter
(** Return true if the node was visited during analysis *)
let is_visited node =
match Summary.get (Procdesc.Node.get_proc_name node) with
match Summary.OnDisk.get (Procdesc.Node.get_proc_name node) with
| None ->
false
| Some summary ->
@ -182,7 +182,7 @@ end = struct
[])
linenum ;
pp_node_link_seq [] ~description:true fmt nodes ;
( match Summary.get pname with
( match Summary.OnDisk.get pname with
| None ->
()
| Some summary ->
@ -230,7 +230,7 @@ end = struct
Hashtbl.replace table_nodes_at_linenum lnum (n :: curr_nodes)
in
List.iter ~f:process_node (Procdesc.get_nodes proc_desc) ;
match Summary.get proc_name with
match Summary.OnDisk.get proc_name with
| None ->
()
| Some summary ->
@ -259,7 +259,7 @@ end = struct
| Procdesc.Node.Start_node ->
let proc_name = Procdesc.Node.get_proc_name n in
let proc_name_escaped = Escape.escape_xml (Typ.Procname.to_string proc_name) in
if Summary.get proc_name |> Option.is_some then (
if Summary.OnDisk.get proc_name |> Option.is_some then (
F.pp_print_char fmt ' ' ;
let label = F.asprintf "summary for %s" proc_name_escaped in
Io_infer.Html.pp_proc_link [fname_encoding] proc_name fmt label )

@ -59,7 +59,7 @@ let log_issue_from_summary severity summary ~node ~session ~loc ~ltr ?extras exn
let log_issue_deprecated_using_state severity proc_name ?node ?loc ?ltr exn =
if !BiabductionConfig.footprint then
match Summary.get proc_name with
match Summary.OnDisk.get proc_name with
| Some summary ->
let node =
let node = match node with None -> State.get_node_exn () | Some node -> node in

@ -519,7 +519,7 @@ let method_exists right_proc_name methods =
| Some attrs ->
attrs.ProcAttributes.is_defined
| None ->
Summary.has_model right_proc_name
Summary.OnDisk.has_model right_proc_name
let resolve_method tenv class_name proc_name =
@ -1125,7 +1125,7 @@ let resolve_and_analyze_clang current_summary tenv prop_r n_actual_params callee
(* to be extended to other methods *)
then
try
let has_clang_model = Summary.has_model callee_pname in
let has_clang_model = Summary.OnDisk.has_model callee_pname in
let resolve_and_analyze_result =
resolve_and_analyze tenv ~caller_summary:current_summary ~has_clang_model prop_r
n_actual_params callee_pname call_flags
@ -1721,7 +1721,7 @@ and unknown_or_scan_call ~is_scan ~reason ret_typ ret_annots
let callee_loc_opt =
Option.map
~f:(fun attributes -> attributes.ProcAttributes.loc)
(Summary.proc_resolve_attributes callee_pname)
(Summary.OnDisk.proc_resolve_attributes callee_pname)
in
let skip_path = Paths.Path.add_skipped_call path callee_pname reason callee_loc_opt in
[(prop_with_undef_attr, skip_path)]
@ -1763,7 +1763,7 @@ and check_variadic_sentinel ?(fails_on_nil = false) n_formals (sentinel, null_po
and check_variadic_sentinel_if_present ({Builtin.prop_; path; proc_name} as builtin_args) =
match Summary.proc_resolve_attributes proc_name with
match Summary.OnDisk.proc_resolve_attributes proc_name with
| None ->
[(prop_, path)]
| Some callee_attributes -> (

@ -834,7 +834,7 @@ let prop_set_exn tenv pname prop se_exn =
(** Include a subtrace for a procedure call if the callee is not a model. *)
let include_subtrace callee_pname =
match Summary.proc_resolve_attributes callee_pname with
match Summary.OnDisk.proc_resolve_attributes callee_pname with
| Some attrs ->
(not attrs.ProcAttributes.is_model)
&& SourceFile.is_under_project_root attrs.ProcAttributes.loc.Location.file

@ -96,9 +96,8 @@ let analysis cfg tenv =
let f proc_name pdesc domain =
if Procdesc.is_defined pdesc && Typ.Procname.is_constructor proc_name then
match
FieldsAssignedInConstructorsChecker.compute_post
(ProcData.make (Summary.reset pdesc) tenv (Ident.Hash.create 10))
~initial
FieldsAssignedInConstructorsChecker.compute_post ~initial
(ProcData.make (Summary.OnDisk.reset pdesc) tenv (Ident.Hash.create 10))
with
| Some new_domain ->
FieldsAssignedInConstructors.union new_domain domain

@ -51,7 +51,8 @@ let is_allocator tenv pname =
let check_attributes check tenv pname =
PatternMatch.check_class_attributes check tenv pname
|| Annotations.pname_has_return_annot pname ~attrs_of_pname:Summary.proc_resolve_attributes check
|| Annotations.pname_has_return_annot pname
~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes check
let method_overrides is_annotated tenv pname =

@ -41,7 +41,7 @@ let protect ~f ~recover ~pp_context (trans_unit_ctx : CFrontend_config.translati
module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFrontend = struct
let model_exists procname = (not Config.models_mode) && Summary.has_model procname
let model_exists procname = (not Config.models_mode) && Summary.OnDisk.has_model procname
(** Translates the method/function's body into nodes of the cfg. *)
let add_method ?(is_destructor_wrapper = false) trans_unit_ctx tenv cfg class_decl_opt procname

@ -524,7 +524,7 @@ let analyze_procedure {Callbacks.exe_env; summary} =
let initial =
let threads =
if
runs_on_ui_thread ~attrs_of_pname:Summary.proc_resolve_attributes tenv proc_desc
runs_on_ui_thread ~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes tenv proc_desc
|> Option.is_some
|| is_thread_confined_method tenv proc_desc
then ThreadsDomain.AnyThreadButSelf

@ -155,7 +155,8 @@ let should_skip =
let has_return_annot predicate pn =
Annotations.pname_has_return_annot pn ~attrs_of_pname:Summary.proc_resolve_attributes predicate
Annotations.pname_has_return_annot pn ~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes
predicate
let is_functional pname =
@ -342,8 +343,8 @@ let is_thread_safe_class pname tenv =
let is_thread_safe_method pname tenv =
find_method_or_override_annotated ~attrs_of_pname:Summary.proc_resolve_attributes is_thread_safe
pname tenv
find_method_or_override_annotated ~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes
is_thread_safe pname tenv
|> Option.is_some

@ -159,8 +159,8 @@ let analyze_procedure {Callbacks.exe_env; summary} =
StarvationDomain.acquire tenv StarvationDomain.bottom loc (Option.to_list lock)
in
let initial =
ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname:Summary.proc_resolve_attributes tenv
proc_desc
ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname:Summary.OnDisk.proc_resolve_attributes
tenv proc_desc
|> Option.value_map ~default:initial ~f:(StarvationDomain.set_on_ui_thread initial loc)
in
let filter_blocks =

@ -41,7 +41,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
~f:(fun attributes ->
ClangMethodKind.equal attributes.ProcAttributes.clang_method_kind
ClangMethodKind.CPP_INSTANCE )
(Summary.proc_resolve_attributes callee_pname)
(Summary.OnDisk.proc_resolve_attributes callee_pname)
let is_objc_instance_method callee_pname =
@ -49,7 +49,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
~f:(fun attributes ->
ClangMethodKind.equal attributes.ProcAttributes.clang_method_kind
ClangMethodKind.OBJC_INSTANCE )
(Summary.proc_resolve_attributes callee_pname)
(Summary.OnDisk.proc_resolve_attributes callee_pname)
let is_blacklisted_method : Typ.Procname.t -> bool =
@ -91,7 +91,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let lookup_local_attributes = function
| Typ.Procname.Java _ as pname ->
(* Looking up the attribute according to the classpath *)
Summary.proc_resolve_attributes pname
Summary.OnDisk.proc_resolve_attributes pname
| pname ->
(* Looking up the attributes locally, i.e. either from the file of from the includes *)
Option.map ~f:Procdesc.get_attributes (Ondemand.get_proc_desc pname)

@ -754,7 +754,7 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
in
let resolved_ret_ =
let ret_ia, ret_typ = callee_annotated_signature.AnnotatedSignature.ret in
let is_library = Summary.proc_is_library callee_attributes in
let is_library = Summary.OnDisk.proc_is_library callee_attributes in
let origin =
TypeOrigin.Proc
{ TypeOrigin.pname= callee_pname

@ -154,7 +154,7 @@ let tests =
; ( "sink without source not tracked"
, [assign_to_non_source "ret_id"; call_sink "ret_id"; assert_empty] ) ]
|> TestInterpreter.create_tests ~pp_opt:pp_sparse
{formal_map= FormalMap.empty; summary= Summary.dummy}
{formal_map= FormalMap.empty; summary= Summary.OnDisk.dummy}
~initial:(MockTaintAnalysis.Domain.bottom, Bindings.empty)
in
"taint_test_suite" >::: test_list

@ -243,7 +243,7 @@ struct
let exit_node = create_node Procdesc.Node.Exit_node [] in
set_succs last_node [exit_node] ~exn_handlers:no_exn_handlers ;
Procdesc.set_exit_node pdesc exit_node ;
(Summary.reset pdesc, assert_map)
(Summary.OnDisk.reset pdesc, assert_map)
let create_test test_program extras ~initial pp_opt test_pname _ =

Loading…
Cancel
Save