From 6047264e4a9d35a95923fcc90fa21467f1b993e9 Mon Sep 17 00:00:00 2001 From: Mehdi Bouaziz Date: Tue, 31 Oct 2017 08:45:23 -0700 Subject: [PATCH] [bug hash] Attempt for a better hash Reviewed By: jeremydubreil Differential Revision: D6162361 fbshipit-source-id: 895d7e4 --- infer/src/IR/Errlog.ml | 2 +- infer/src/IR/Errlog.mli | 4 +- infer/src/IR/Exceptions.ml | 2 +- infer/src/IR/Exceptions.mli | 2 +- infer/src/atd/jsonbug.atd | 4 +- infer/src/backend/Differential.ml | 2 +- infer/src/backend/DifferentialFilters.ml | 4 +- infer/src/backend/InferPrint.ml | 22 ++-- infer/src/backend/reporting.ml | 22 ++-- infer/src/backend/reporting.mli | 2 +- infer/src/backend/state.ml | 61 +++++----- infer/src/backend/state.mli | 7 +- infer/src/base/Utils.ml | 2 + infer/src/base/Utils.mli | 3 + infer/src/clang/cFrontend_errors.ml | 2 +- infer/src/unit/DifferentialFiltersTests.ml | 106 +++++++++--------- infer/src/unit/DifferentialTests.ml | 28 ++--- infer/src/unit/DifferentialTestsUtils.ml | 19 ++-- infer/tests/build_systems/ant/issues.exp | 2 +- .../tests/codetoanalyze/java/infer/issues.exp | 2 +- .../codetoanalyze/objc/errors/issues.exp | 2 +- 21 files changed, 160 insertions(+), 140 deletions(-) diff --git a/infer/src/IR/Errlog.ml b/infer/src/IR/Errlog.ml index ef5fa4dd4..619823b9f 100644 --- a/infer/src/IR/Errlog.ml +++ b/infer/src/IR/Errlog.ml @@ -65,7 +65,7 @@ let compute_local_exception_line loc_trace = snd (List_.fold_until ~init:(`Continue (None, None)) ~f:compute_local_exception_line loc_trace) -type node_id_key = {node_id: int; node_key: int} +type node_id_key = {node_id: int; node_key: Digest.t} type err_key = { err_kind: Exceptions.err_kind diff --git a/infer/src/IR/Errlog.mli b/infer/src/IR/Errlog.mli index 59cbd8b3c..3f1132177 100644 --- a/infer/src/IR/Errlog.mli +++ b/infer/src/IR/Errlog.mli @@ -36,7 +36,7 @@ val compute_local_exception_line : loc_trace -> int option This extra information adds value to the report itself, and may avoid digging into the trace to understand the cause of the report. *) -type node_id_key = private {node_id: int; node_key: int} +type node_id_key = private {node_id: int; node_key: Digest.t} type err_key = private { err_kind: Exceptions.err_kind @@ -93,7 +93,7 @@ val update : t -> t -> unit (** Update an old error log with a new one *) val log_issue : - Exceptions.err_kind -> t -> Location.t -> int * int -> int -> loc_trace + Exceptions.err_kind -> t -> Location.t -> int * Digest.t -> int -> loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn -> unit (** {2 Functions for manipulating per-file error tables} *) diff --git a/infer/src/IR/Exceptions.ml b/infer/src/IR/Exceptions.ml index b78e8aa71..0caaeea27 100644 --- a/infer/src/IR/Exceptions.ml +++ b/infer/src/IR/Exceptions.ml @@ -686,7 +686,7 @@ let print_key = false (** pretty print an error *) let pp_err ~node_key loc ekind ex_name desc ml_loc_opt fmt () = let kind = err_kind_string (if equal_err_kind ekind Kinfo then Kwarning else ekind) in - let pp_key fmt k = if print_key then F.fprintf fmt " key: %d " k else () in + let pp_key fmt k = if print_key then F.fprintf fmt " key: %s " (Digest.to_hex k) else () in F.fprintf fmt "%a:%d: %s: %a %a%a%a@\n" SourceFile.pp loc.Location.file loc.Location.line kind IssueType.pp ex_name Localise.pp_error_desc desc pp_key node_key L.pp_ml_loc_opt ml_loc_opt diff --git a/infer/src/IR/Exceptions.mli b/infer/src/IR/Exceptions.mli index c71409f26..4dbb90bae 100644 --- a/infer/src/IR/Exceptions.mli +++ b/infer/src/IR/Exceptions.mli @@ -158,7 +158,7 @@ val print_exception_html : string -> exn -> unit (** print a description of the exception to the html output *) val pp_err : - node_key:int -> Location.t -> err_kind -> IssueType.t -> Localise.error_desc + node_key:Digest.t -> Location.t -> err_kind -> IssueType.t -> Localise.error_desc -> Logging.ml_loc option -> Format.formatter -> unit -> unit (** pretty print an error *) diff --git a/infer/src/atd/jsonbug.atd b/infer/src/atd/jsonbug.atd index fa8d43abb..2dc812275 100644 --- a/infer/src/atd/jsonbug.atd +++ b/infer/src/atd/jsonbug.atd @@ -34,9 +34,9 @@ type jsonbug = { procedure_start_line : int; file : string; bug_trace : json_trace_item list; - key : int; + key : string; qualifier_tags : tag_value_record list; - hash : int; + hash : string; ?dotty : string option; ?infer_source_loc: loc option; bug_type_hum: string; diff --git a/infer/src/backend/Differential.ml b/infer/src/backend/Differential.ml index 7bdf5d859..c7a0c2aab 100644 --- a/infer/src/backend/Differential.ml +++ b/infer/src/backend/Differential.ml @@ -16,7 +16,7 @@ let of_reports ~(current_report: Jsonbug_t.report) ~(previous_report: Jsonbug_t. let to_map report = List.fold_left ~f:(fun map issue -> Map.add_multi map ~key:issue.Jsonbug_t.hash ~data:issue) - ~init:Int.Map.empty report + ~init:String.Map.empty report in let fold_aux ~key:_ ~data (left, both, right) = match data with diff --git a/infer/src/backend/DifferentialFilters.ml b/infer/src/backend/DifferentialFilters.ml index e786c961e..15c786cf2 100644 --- a/infer/src/backend/DifferentialFilters.ml +++ b/infer/src/backend/DifferentialFilters.ml @@ -123,7 +123,7 @@ let skip_duplicated_types_on_filenames renamings (diff: Differential.t) : Differ String.compare f1 f2 in let cmp ((issue1, _) as issue_with_previous_file1) ((issue2, _) as issue_with_previous_file2) = - [%compare : int * string * issue_file_with_renaming] + [%compare : Digest.t * string * issue_file_with_renaming] (issue1.Jsonbug_t.key, issue1.Jsonbug_t.bug_type, issue_with_previous_file1) (issue2.Jsonbug_t.key, issue2.Jsonbug_t.bug_type, issue_with_previous_file2) in @@ -180,7 +180,7 @@ let value_of_qualifier_tag qts tag = type file_extension = string [@@deriving compare] -type weak_hash = string * string * string * int * string option [@@deriving compare] +type weak_hash = string * string * string * Digest.t * string option [@@deriving compare] let skip_anonymous_class_renamings (diff: Differential.t) : Differential.t = (* diff --git a/infer/src/backend/InferPrint.ml b/infer/src/backend/InferPrint.ml index 6f8bf998e..c8a33b366 100644 --- a/infer/src/backend/InferPrint.ml +++ b/infer/src/backend/InferPrint.ml @@ -73,10 +73,10 @@ let error_desc_to_xml_tags error_desc = let get_bug_hash (kind: string) (type_str: string) (procedure_id: string) (filename: string) - (node_key: int) (error_desc: Localise.error_desc) = + (node_key: Digest.t) (error_desc: Localise.error_desc) = let qualifier_tag_call_procedure = Localise.error_desc_get_tag_call_procedure error_desc in let qualifier_tag_value = Localise.error_desc_get_tag_value error_desc in - Hashtbl.hash + Utils.better_hash ( kind , type_str , procedure_id @@ -309,11 +309,12 @@ module IssuesCsv = struct pp "\"%s\"," (Escape.escape_csv procedure_id) ; pp "%s," filename ; pp "\"%s\"," (Escape.escape_csv trace) ; - pp "\"%d\"," err_data.node_id_key.node_key ; + pp "\"%s\"," (Digest.to_hex err_data.node_id_key.node_key) ; pp "\"%s\"," qualifier_tag_xml ; - pp "\"%d\"," - (get_bug_hash kind type_str procedure_id filename err_data.node_id_key.node_key - key.err_desc) ; + pp "\"%s\"," + ( get_bug_hash kind type_str procedure_id filename err_data.node_id_key.node_key + key.err_desc + |> Digest.to_hex ) ; pp "\"%d\"," !csv_issues_id ; (* bug id *) pp "\"%s\"," always_report ; @@ -399,10 +400,11 @@ module IssuesJson = struct ; procedure_start_line ; file ; bug_trace= loc_trace_to_jsonbug_record err_data.loc_trace key.err_kind - ; key= err_data.node_id_key.node_key + ; key= err_data.node_id_key.node_key |> Digest.to_hex ; qualifier_tags= Localise.Tags.tag_value_records_of_tags key.err_desc.tags ; hash= get_bug_hash kind bug_type procedure_id file err_data.node_id_key.node_key key.err_desc + |> Digest.to_hex ; dotty= error_desc_to_dotty_string key.err_desc ; infer_source_loc= json_ml_loc ; bug_type_hum= key.err_name.IssueType.hum @@ -460,9 +462,9 @@ let pp_custom_of_report fmt report fields = | `Issue_field_bug_trace -> pp_trace fmt issue.bug_trace (comma_separator index) | `Issue_field_key -> - Format.fprintf fmt "%s%d" (comma_separator index) issue.key + Format.fprintf fmt "%s%s" (comma_separator index) (Digest.to_hex issue.key) | `Issue_field_hash -> - Format.fprintf fmt "%s%d" (comma_separator index) issue.hash + Format.fprintf fmt "%s%s" (comma_separator index) (Digest.to_hex issue.hash) | `Issue_field_line_offset -> Format.fprintf fmt "%s%d" (comma_separator index) (issue.line - issue.procedure_start_line) @@ -479,7 +481,7 @@ let pp_custom_of_report fmt report fields = let tests_jsonbug_compare bug1 bug2 = let open Jsonbug_t in - [%compare : string * string * int * string * int] + [%compare : string * string * int * string * Digest.t] (bug1.file, bug1.procedure, bug1.line - bug1.procedure_start_line, bug1.bug_type, bug1.hash) (bug2.file, bug2.procedure, bug2.line - bug2.procedure_start_line, bug2.bug_type, bug2.hash) diff --git a/infer/src/backend/reporting.ml b/infer/src/backend/reporting.ml index cf4d7d721..375a1c105 100644 --- a/infer/src/backend/reporting.ml +++ b/infer/src/backend/reporting.ml @@ -11,23 +11,27 @@ open! IStd module L = Logging type log_t = - ?loc:Location.t -> ?node_id:int * int -> ?session:int -> ?ltr:Errlog.loc_trace + ?loc:Location.t -> ?node_id:int * Digest.t -> ?session:int -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn -> unit type log_issue_from_errlog = Errlog.t -> log_t let log_issue_from_errlog err_kind err_log ?loc ?node_id ?session ?ltr ?linters_def_file ?doc_url exn = - let loc = match loc with None -> State.get_loc () | Some loc -> loc in - let node_id = - match node_id with None -> (State.get_node_id_key () :> int * int) | Some node_id -> node_id - in - let session = - match session with None -> (State.get_session () :> int) | Some session -> session - in - let ltr = match ltr with None -> State.get_loc_trace () | Some ltr -> ltr in let issue_type = (Exceptions.recognize_exception exn).name in if not Config.filtering (* no-filtering takes priority *) || issue_type.IssueType.enabled then + let loc = match loc with None -> State.get_loc () | Some loc -> loc in + let node_id = + match node_id with + | None -> + (State.get_node_id_key () :> int * Digest.t) + | Some node_id -> + node_id + in + let session = + match session with None -> (State.get_session () :> int) | Some session -> session + in + let ltr = match ltr with None -> State.get_loc_trace () | Some ltr -> ltr in Errlog.log_issue err_kind err_log loc node_id session ltr ?linters_def_file ?doc_url exn diff --git a/infer/src/backend/reporting.mli b/infer/src/backend/reporting.mli index cdec3de5e..d5073c582 100644 --- a/infer/src/backend/reporting.mli +++ b/infer/src/backend/reporting.mli @@ -12,7 +12,7 @@ open! IStd (** Type of functions to report issues to the error_log in a spec. *) type log_t = - ?loc:Location.t -> ?node_id:int * int -> ?session:int -> ?ltr:Errlog.loc_trace + ?loc:Location.t -> ?node_id:int * Digest.t -> ?session:int -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn -> unit type log_issue_from_errlog = Errlog.t -> log_t diff --git a/infer/src/backend/state.ml b/infer/src/backend/state.ml index 54bbcd7be..c418c77ee 100644 --- a/infer/src/backend/state.ml +++ b/infer/src/backend/state.ml @@ -27,7 +27,7 @@ type failure_stats = ; (* number of node failures (i.e. at least one instruction failure) *) mutable node_ok: int ; (* number of node successes (i.e. no instruction failures) *) - mutable first_failure: (Location.t * (int * int) * int * Errlog.loc_trace * exn) option + mutable first_failure: (Location.t * (int * Digest.t) * int * Errlog.loc_trace * exn) option (* exception at the first failure *) } module NodeHash = Procdesc.NodeHash @@ -106,31 +106,31 @@ let get_node () = !gs.last_node (** simple key for a node: just look at the instructions *) let node_simple_key node = - let key = ref [] in - let add_key k = key := k :: !key in - let do_instr instr = - if Sil.instr_is_auxiliary instr then () + let add_instr key instr = + if Sil.instr_is_auxiliary instr then key else - match instr with - | Sil.Load _ -> - add_key 1 - | Sil.Store _ -> - add_key 2 - | Sil.Prune _ -> - add_key 3 - | Sil.Call _ -> - add_key 4 - | Sil.Nullify _ -> - add_key 5 - | Sil.Abstract _ -> - add_key 6 - | Sil.Remove_temps _ -> - add_key 7 - | Sil.Declare_locals _ -> - add_key 8 + let instr_key = + match instr with + | Sil.Load _ -> + 1 + | Sil.Store _ -> + 2 + | Sil.Prune _ -> + 3 + | Sil.Call _ -> + 4 + | Sil.Nullify _ -> + 5 + | Sil.Abstract _ -> + 6 + | Sil.Remove_temps _ -> + 7 + | Sil.Declare_locals _ -> + 8 + in + instr_key :: key in - List.iter ~f:do_instr (Procdesc.Node.get_instrs node) ; - Hashtbl.hash !key + Procdesc.Node.get_instrs node |> List.fold ~init:[] ~f:add_instr |> Utils.better_hash (** key for a node: look at the current node, successors and predecessors *) @@ -138,9 +138,11 @@ let node_key node = let succs = Procdesc.Node.get_succs node in let preds = Procdesc.Node.get_preds node in let v = - (node_simple_key node, List.map ~f:node_simple_key succs, List.map ~f:node_simple_key preds) + ( node_simple_key node + , List.rev_map ~f:node_simple_key succs + , List.rev_map ~f:node_simple_key preds ) in - Hashtbl.hash v + Utils.better_hash v (** normalize the list of instructions by renaming let-bound ids *) @@ -320,7 +322,7 @@ let mark_instr_ok () = let mark_instr_fail exn = let loc = get_loc () in - let key = (get_node_id_key () :> int * int) in + let key = (get_node_id_key () :> int * Digest.t) in let session = get_session () in let loc_trace = get_loc_trace () in let fs = get_failure_stats (get_node ()) in @@ -330,8 +332,9 @@ let mark_instr_fail exn = type log_issue = - ?store_summary:bool -> Typ.Procname.t -> ?loc:Location.t -> ?node_id:int * int -> ?session:int - -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn -> unit + ?store_summary:bool -> Typ.Procname.t -> ?loc:Location.t -> ?node_id:int * Digest.t + -> ?session:int -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn + -> unit let process_execution_failures (log_issue: log_issue) pname = let do_failure _ fs = diff --git a/infer/src/backend/state.mli b/infer/src/backend/state.mli index b107c375e..39a93758d 100644 --- a/infer/src/backend/state.mli +++ b/infer/src/backend/state.mli @@ -47,7 +47,7 @@ val get_node : unit -> Procdesc.Node.t val get_node_id : unit -> Procdesc.Node.id (** Get id of last node seen in symbolic execution *) -val get_node_id_key : unit -> Procdesc.Node.id * int +val get_node_id_key : unit -> Procdesc.Node.id * Digest.t (** Get id and key of last node seen in symbolic execution *) val get_normalized_pre : @@ -85,8 +85,9 @@ val mk_find_duplicate_nodes : Procdesc.t -> Procdesc.Node.t -> Procdesc.NodeSet. and normalized (w.r.t. renaming of let - bound ids) list of instructions. *) type log_issue = - ?store_summary:bool -> Typ.Procname.t -> ?loc:Location.t -> ?node_id:int * int -> ?session:int - -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn -> unit + ?store_summary:bool -> Typ.Procname.t -> ?loc:Location.t -> ?node_id:int * Digest.t + -> ?session:int -> ?ltr:Errlog.loc_trace -> ?linters_def_file:string -> ?doc_url:string -> exn + -> unit val process_execution_failures : log_issue -> Typ.Procname.t -> unit (** Process the failures during symbolic execution of a procedure *) diff --git a/infer/src/base/Utils.ml b/infer/src/base/Utils.ml index 0f0085aee..0a46bb2a7 100644 --- a/infer/src/base/Utils.ml +++ b/infer/src/base/Utils.ml @@ -378,3 +378,5 @@ let without_gc ~f = let yield () = Unix.select ~read:[] ~write:[] ~except:[] ~timeout:(`After Time_ns.Span.min_value) |> ignore + +let better_hash x = Marshal.to_string x [Marshal.No_sharing] |> Digest.string diff --git a/infer/src/base/Utils.mli b/infer/src/base/Utils.mli index b975e9c86..af9d49710 100644 --- a/infer/src/base/Utils.mli +++ b/infer/src/base/Utils.mli @@ -107,3 +107,6 @@ val without_gc : f:(unit -> unit) -> unit val yield : unit -> unit (** try to give the control back to the OS without sleeping too much *) + +val better_hash : 'a -> Digest.t +(** Hashtbl.hash only hashes the first 10 meaningful values, [better_hash] uses everything. *) diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index 0f25ef0a1..6cda8321f 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -461,7 +461,7 @@ let log_frontend_issue translation_unit_context method_decl_opt (node: Ctl_parse | Stmt st -> CAst_utils.generate_key_stmt st in - let key = Hashtbl.hash key_str in + let key = Utils.better_hash key_str in Reporting.log_issue_from_errlog err_kind errlog exn ~loc:issue_desc.loc ~ltr:trace ~node_id:(0, key) ?linters_def_file ?doc_url:issue_desc.doc_url diff --git a/infer/src/unit/DifferentialFiltersTests.ml b/infer/src/unit/DifferentialFiltersTests.ml index 3dcf5b02f..70a079acd 100644 --- a/infer/src/unit/DifferentialFiltersTests.ml +++ b/infer/src/unit/DifferentialFiltersTests.ml @@ -149,19 +149,19 @@ let test_relative_complements = let test_skip_duplicated_types_on_filenames = let current_report = - [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:22 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:11 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:111 () - ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_4.java" ~hash:4 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:222 () - ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:55 () ] + [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:"22" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:"11" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:"111" () + ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_4.java" ~hash:"4" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:"222" () + ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:"55" () ] in let previous_report = - [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:222 () - ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:5 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:1 () - ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_3.java" ~hash:3 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2.java" ~hash:2 () ] + [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:"222" () + ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:"5" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:"1" () + ; create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_3.java" ~hash:"3" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2.java" ~hash:"2" () ] in let renamings = DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.from_renamings @@ -175,16 +175,16 @@ let test_skip_duplicated_types_on_filenames = in let do_assert _ = assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of introduced") - [4] + ~pp_diff:(pp_diff_of_string_list "Hashes of introduced") + ["4"] (sorted_hashes_of_issues diff'.introduced) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of fixed") - [3] + ~pp_diff:(pp_diff_of_string_list "Hashes of fixed") + ["3"] (sorted_hashes_of_issues diff'.fixed) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of preexisting") - [22; 55; 111; 222] + ~pp_diff:(pp_diff_of_string_list "Hashes of preexisting") + ["111"; "22"; "222"; "55"] (sorted_hashes_of_issues diff'.preexisting) in "test_skip_duplicated_types_on_filenames" >:: do_assert @@ -219,15 +219,15 @@ let test_skip_anonymous_class_renamings = input_diff in assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of introduced") + ~pp_diff:(pp_diff_of_string_list "Hashes of introduced") exp_introduced (sorted_hashes_of_issues diff'.introduced) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of fixed") + ~pp_diff:(pp_diff_of_string_list "Hashes of fixed") exp_fixed (sorted_hashes_of_issues diff'.fixed) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of preexisting") + ~pp_diff:(pp_diff_of_string_list "Hashes of preexisting") exp_preexisting (sorted_hashes_of_issues diff'.preexisting) in @@ -240,112 +240,112 @@ let test_skip_anonymous_class_renamings = ( "com.whatever.package00.abcd." ^ "ABasicExampleFragment$83.onMenuItemActionExpand(android.view.MenuItem):b." ^ "5ab5e18cae498c35d887ce88f3d5fa82" ) - ~file:"a.java" ~key:1 ~qualifier_tags:qt1 ~hash:3 () + ~file:"a.java" ~key:"1" ~qualifier_tags:qt1 ~hash:"3" () ; create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: ( "com.whatever.package00.abcd." ^ "ABasicExampleFragment$83$7.onMenuItemActionExpand(android.view.MenuItem)." ^ "522cc747174466169781c9d2fc980dbc" ) - ~file:"a.java" ~key:1 ~qualifier_tags:qt1 ~hash:4 () + ~file:"a.java" ~key:"1" ~qualifier_tags:qt1 ~hash:"4" () ; create_fake_jsonbug ~bug_type:"bug_type_2" - ~procedure_id:"procid5.c854fd4a98113d9ab5b82deb3545de89" ~file:"b.java" ~key:5 - ~hash:5 () ] + ~procedure_id:"procid5.c854fd4a98113d9ab5b82deb3545de89" ~file:"b.java" ~key:"5" + ~hash:"5" () ] ~previous_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: ( "com.whatever.package00.abcd." ^ "ABasicExampleFragment$9.onMenuItemActionExpand(android.view.MenuItem):bo." ^ "ba1776155fba2899542401da5bc779a5" ) - ~file:"a.java" ~key:1 ~qualifier_tags:qt1 ~hash:1 () + ~file:"a.java" ~key:"1" ~qualifier_tags:qt1 ~hash:"1" () ; create_fake_jsonbug ~bug_type:"bug_type_2" - ~procedure_id:"procid2.92095aee3f1884c37e96feae031f4931" ~file:"b.java" ~key:2 - ~hash:2 () ] - , ([4; 5], [2], [3]) ) + ~procedure_id:"procid2.92095aee3f1884c37e96feae031f4931" ~file:"b.java" ~key:"2" + ~hash:"2" () ] + , (["4"; "5"], ["2"], ["3"]) ) ; ( "test_skip_anonymous_class_renamings_with_empty_qualifier_tags" , Differential.of_reports ~current_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f" - ~file:"a.java" ~key:1 ~hash:1 () + ~file:"a.java" ~key:"1" ~hash:"1" () ; create_fake_jsonbug ~bug_type:"bug_type_2" ~procedure_id: "com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f" - ~file:"a.java" ~key:1 ~hash:3 () ] + ~file:"a.java" ~key:"1" ~hash:"3" () ] ~previous_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03" - ~file:"a.java" ~key:1 ~hash:2 () + ~file:"a.java" ~key:"1" ~hash:"2" () ; create_fake_jsonbug ~bug_type:"bug_type_2" ~procedure_id: "com.whatever.package.Class$8.foo():bool.cffd4e941668063eb802183dbd3e856d" - ~file:"a.java" ~key:1 ~hash:4 () ] - , ([1], [2], [3]) ) + ~file:"a.java" ~key:"1" ~hash:"4" () ] + , (["1"], ["2"], ["3"]) ) ; ( "test_skip_anonymous_class_renamings_with_matching_non_anonymous_procedure_ids" , Differential.of_reports ~current_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247" - ~file:"a.java" ~key:1 ~hash:1 () + ~file:"a.java" ~key:"1" ~hash:"1" () ; create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247" - ~file:"a.java" ~key:1 ~hash:3 () ] + ~file:"a.java" ~key:"1" ~hash:"3" () ] ~previous_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247" - ~file:"a.java" ~key:1 ~hash:2 () + ~file:"a.java" ~key:"1" ~hash:"2" () ; create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247" - ~file:"a.java" ~key:1 ~hash:4 () ] - , ([1; 3], [2; 4], []) ) + ~file:"a.java" ~key:"1" ~hash:"4" () ] + , (["1"; "3"], ["2"; "4"], []) ) ; ( "test_skip_anonymous_class_renamings_with_non_java_files" , Differential.of_reports ~current_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$3$1.foo():bool.9ff39eb5c53c81da9f6a7ade324345b6" - ~file:"a.java" ~key:1 ~hash:1 () + ~file:"a.java" ~key:"1" ~hash:"1" () ; create_fake_jsonbug ~bug_type:"bug_type_2" ~procedure_id: "com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f" - ~file:"a.mm" ~key:1 ~hash:3 () ] + ~file:"a.mm" ~key:"1" ~hash:"3" () ] ~previous_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03" - ~file:"a.java" ~key:1 ~hash:2 () + ~file:"a.java" ~key:"1" ~hash:"2" () ; create_fake_jsonbug ~bug_type:"bug_type_2" ~procedure_id: "com.whatever.package.Class$8.foo():bool.cffd4e941668063eb802183dbd3e856d" - ~file:"a.mm" ~key:1 ~hash:4 () ] - , ([3], [4], [1]) ) + ~file:"a.mm" ~key:"1" ~hash:"4" () ] + , (["3"], ["4"], ["1"]) ) ; ( "test_skip_anonymous_class_renamings_with_different_call_procedure_qualifier_tags" , Differential.of_reports ~current_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$3$1.foo():bool.9ff39eb5c53c81da9f6a7ade324345b6" - ~file:"a.java" ~key:1 ~qualifier_tags:qt1 ~hash:1 () ] + ~file:"a.java" ~key:"1" ~qualifier_tags:qt1 ~hash:"1" () ] ~previous_report: [ create_fake_jsonbug ~bug_type:"bug_type_1" ~procedure_id: "com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03" - ~file:"a.java" ~key:1 ~qualifier_tags:qt2 ~hash:2 () ] - , ([1], [2], []) ) ] + ~file:"a.java" ~key:"1" ~qualifier_tags:qt2 ~hash:"2" () ] + , (["1"], ["2"], []) ) ] |> List.map ~f:(fun (name, diff, expected_output) -> name >:: create_test diff expected_output) let test_interesting_paths_filter = let report = - [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:1 () + [ create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:"1" () ; create_fake_jsonbug ~bug_type:IssueType.null_dereference.unique_id ~file:"file_2.java" - ~hash:2 () - ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_4.java" ~hash:4 () ] + ~hash:"2" () + ; create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_4.java" ~hash:"4" () ] in let create_test interesting_paths expected_hashes _ = let filter = @@ -353,15 +353,17 @@ let test_interesting_paths_filter = interesting_paths in let filtered_report = filter report in - assert_equal ~pp_diff:(pp_diff_of_int_list "Bug hash") expected_hashes + assert_equal + ~pp_diff:(pp_diff_of_string_list "Bug hash") + expected_hashes (sorted_hashes_of_issues filtered_report) in - [ ("test_interesting_paths_filter_with_none_interesting_paths", None, [1; 2; 4]) + [ ("test_interesting_paths_filter_with_none_interesting_paths", None, ["1"; "2"; "4"]) ; ( "test_interesting_paths_filter_with_some_interesting_paths" , Some [ SourceFile.create ~warn_on_error:false "file_not_existing.java" ; SourceFile.create ~warn_on_error:false "file_4.java" ] - , [4] ) + , ["4"] ) ; ( "test_interesting_paths_filter_with_some_interesting_paths_that_are_not_in_report" , Some [ SourceFile.create ~warn_on_error:false "file_not_existing.java" diff --git a/infer/src/unit/DifferentialTests.ml b/infer/src/unit/DifferentialTests.ml index 43f1c63e5..a97b5d897 100644 --- a/infer/src/unit/DifferentialTests.ml +++ b/infer/src/unit/DifferentialTests.ml @@ -12,15 +12,17 @@ open OUnit2 open DifferentialTestsUtils let current_report = - [ create_fake_jsonbug ~hash:3 () - ; create_fake_jsonbug ~hash:1 () - ; create_fake_jsonbug ~hash:2 () - ; create_fake_jsonbug ~hash:2 () - ; create_fake_jsonbug ~hash:2 () ] + [ create_fake_jsonbug ~hash:"3" () + ; create_fake_jsonbug ~hash:"1" () + ; create_fake_jsonbug ~hash:"2" () + ; create_fake_jsonbug ~hash:"2" () + ; create_fake_jsonbug ~hash:"2" () ] let previous_report = - [create_fake_jsonbug ~hash:1 (); create_fake_jsonbug ~hash:4 (); create_fake_jsonbug ~hash:1 ()] + [ create_fake_jsonbug ~hash:"1" () + ; create_fake_jsonbug ~hash:"4" () + ; create_fake_jsonbug ~hash:"1" () ] let diff = Differential.of_reports ~current_report ~previous_report @@ -30,7 +32,7 @@ let test_diff_keeps_duplicated_hashes = let hashes_expected = 3 in let hashes_found = List.fold ~init:0 - ~f:(fun acc i -> if Int.equal i.Jsonbug_t.hash 2 then acc + 1 else acc) + ~f:(fun acc i -> if String.equal i.Jsonbug_t.hash "2" then acc + 1 else acc) diff.introduced in let pp_diff fmt (expected, actual) = @@ -45,16 +47,16 @@ let test_diff_keeps_duplicated_hashes = let test_set_operations = let do_assert _ = assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of introduced") - [2; 2; 2; 3] + ~pp_diff:(pp_diff_of_string_list "Hashes of introduced") + ["2"; "2"; "2"; "3"] (sorted_hashes_of_issues diff.introduced) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of fixed") - [4] + ~pp_diff:(pp_diff_of_string_list "Hashes of fixed") + ["4"] (sorted_hashes_of_issues diff.fixed) ; assert_equal - ~pp_diff:(pp_diff_of_int_list "Hashes of preexisting") - [1] + ~pp_diff:(pp_diff_of_string_list "Hashes of preexisting") + ["1"] (sorted_hashes_of_issues diff.preexisting) in "test_set_operations" >:: do_assert diff --git a/infer/src/unit/DifferentialTestsUtils.ml b/infer/src/unit/DifferentialTestsUtils.ml index 344adfe94..df7d57bfb 100644 --- a/infer/src/unit/DifferentialTestsUtils.ml +++ b/infer/src/unit/DifferentialTestsUtils.ml @@ -12,8 +12,8 @@ open! IStd let create_fake_jsonbug ?(bug_class= "bug_class") ?(kind= "kind") ?(bug_type= "bug_type") ?(qualifier= "qualifier") ?(severity= "severity") ?(visibility= "visibility") ?(line= 1) ?(column= 1) ?(procedure= "procedure") ?(procedure_id= "procedure_id") - ?(procedure_start_line= 1) ?(file= "file/at/a/certain/path.java") ?(bug_trace= []) ?(key= 1234) - ?(qualifier_tags= []) ?(hash= 1) ?(dotty= None) ?(infer_source_loc= None) + ?(procedure_start_line= 1) ?(file= "file/at/a/certain/path.java") ?(bug_trace= []) + ?(key= "1234") ?(qualifier_tags= []) ?(hash= "1") ?(dotty= None) ?(infer_source_loc= None) ?(linters_def_file= Some "file/at/certain/path.al") ?doc_url () : Jsonbug_t.jsonbug = { bug_class ; kind @@ -39,16 +39,17 @@ let create_fake_jsonbug ?(bug_class= "bug_class") ?(kind= "kind") ?(bug_type= "b ; traceview_id= None } -let pp_diff_of_int_list group_name fmt (expected, actual) = - Format.fprintf fmt "[%s]: Expected: [%a] Found: [%a]" group_name - (Pp.comma_seq Format.pp_print_int) - expected - (Pp.comma_seq Format.pp_print_int) - actual +let pp_diff_of_list ~pp group_name fmt (expected, actual) = + Format.fprintf fmt "[%s]: Expected: [%a] Found: [%a]" group_name (Pp.comma_seq pp) expected + (Pp.comma_seq pp) actual +let pp_diff_of_string_list = pp_diff_of_list ~pp:Format.pp_print_string + +let pp_diff_of_int_list = pp_diff_of_list ~pp:Format.pp_print_int + (* Sort hashes to make things easier to compare *) let sorted_hashes_of_issues issues = let hash i = i.Jsonbug_t.hash in - List.sort ~cmp:Int.compare (List.map ~f:hash issues) + List.sort ~cmp:String.compare (List.rev_map ~f:hash issues) diff --git a/infer/tests/build_systems/ant/issues.exp b/infer/tests/build_systems/ant/issues.exp index 96f3e5cf7..04bd71890 100644 --- a/infer/tests/build_systems/ant/issues.exp +++ b/infer/tests/build_systems/ant/issues.exp @@ -158,8 +158,8 @@ codetoanalyze/java/infer/ResourceLeaks.java, int ResourceLeaks.fileOutputStreamT codetoanalyze/java/infer/ResourceLeaks.java, int ResourceLeaks.readConfigNotCloseStream(String), 5, RESOURCE_LEAK, [start of procedure readConfigNotCloseStream(...)] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.activityObtainTypedArrayAndLeak(Activity), 2, RESOURCE_LEAK, [start of procedure activityObtainTypedArrayAndLeak(...),start of procedure ignore(...),return from a call to void ResourceLeaks.ignore(TypedArray)] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.contextObtainTypedArrayAndLeak(Context), 2, RESOURCE_LEAK, [start of procedure contextObtainTypedArrayAndLeak(...),start of procedure ignore(...),return from a call to void ResourceLeaks.ignore(TypedArray)] -codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),Skipping transferTo(...): unknown method,Taking true branch,exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),exception java.io.FileNotFoundException] +codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),Skipping transferTo(...): unknown method,Taking true branch,exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.deflaterLeak(), 1, RESOURCE_LEAK, [start of procedure deflaterLeak()] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.fileInputStreamNotClosedAfterRead(), 6, RESOURCE_LEAK, [start of procedure fileInputStreamNotClosedAfterRead(),exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.fileOutputStreamNotClosed(), 1, RESOURCE_LEAK, [start of procedure fileOutputStreamNotClosed()] diff --git a/infer/tests/codetoanalyze/java/infer/issues.exp b/infer/tests/codetoanalyze/java/infer/issues.exp index fce43d4b2..1a5600ac8 100644 --- a/infer/tests/codetoanalyze/java/infer/issues.exp +++ b/infer/tests/codetoanalyze/java/infer/issues.exp @@ -168,8 +168,8 @@ codetoanalyze/java/infer/ResourceLeaks.java, int ResourceLeaks.readConfigNotClos codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.NoResourceLeakWarningAfterCheckState(File,int), 2, PRECONDITION_NOT_MET, [start of procedure NoResourceLeakWarningAfterCheckState(...),Taking false branch] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.activityObtainTypedArrayAndLeak(Activity), 2, RESOURCE_LEAK, [start of procedure activityObtainTypedArrayAndLeak(...),start of procedure ignore(...),return from a call to void ResourceLeaks.ignore(TypedArray)] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.contextObtainTypedArrayAndLeak(Context), 2, RESOURCE_LEAK, [start of procedure contextObtainTypedArrayAndLeak(...),start of procedure ignore(...),return from a call to void ResourceLeaks.ignore(TypedArray)] -codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),Skipping transferTo(...): unknown method,Taking true branch,exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),exception java.io.FileNotFoundException] +codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.copyFileLeak(File,File), 11, RESOURCE_LEAK, [start of procedure copyFileLeak(...),Skipping transferTo(...): unknown method,Taking true branch,exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.deflaterLeak(), 1, RESOURCE_LEAK, [start of procedure deflaterLeak()] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.fileInputStreamNotClosedAfterRead(), 6, RESOURCE_LEAK, [start of procedure fileInputStreamNotClosedAfterRead(),exception java.io.IOException] codetoanalyze/java/infer/ResourceLeaks.java, void ResourceLeaks.fileOutputStreamNotClosed(), 1, RESOURCE_LEAK, [start of procedure fileOutputStreamNotClosed()] diff --git a/infer/tests/codetoanalyze/objc/errors/issues.exp b/infer/tests/codetoanalyze/objc/errors/issues.exp index 91523bbbf..2942094ed 100644 --- a/infer/tests/codetoanalyze/objc/errors/issues.exp +++ b/infer/tests/codetoanalyze/objc/errors/issues.exp @@ -23,8 +23,8 @@ codetoanalyze/objc/errors/warnings/ParameterNotNullableExample.m, FBAudioRecorde codetoanalyze/objc/errors/warnings/ParameterNotNullableExample.m, FBAudioRecorder_test, 3, NULL_DEREFERENCE, [start of procedure test,Message recordState with receiver nil returns nil.] codetoanalyze/objc/shared/assertions/NSAssert_example.m, NSAssert_addTarget:, 1, MEMORY_LEAK, [start of procedure addTarget:,Condition is false,Condition is true,Condition is true] codetoanalyze/objc/shared/assertions/NSAssert_example.m, NSAssert_initWithRequest:, 1, MEMORY_LEAK, [start of procedure initWithRequest:,Condition is false,Condition is true,Condition is true] -codetoanalyze/objc/shared/assertions/NSAssert_example.m, test1, 1, MEMORY_LEAK, [start of procedure test1(),Condition is false,Condition is true,Condition is true,Condition is true] codetoanalyze/objc/shared/assertions/NSAssert_example.m, test1, 1, MEMORY_LEAK, [start of procedure test1(),Condition is false,Condition is true,Condition is true] +codetoanalyze/objc/shared/assertions/NSAssert_example.m, test1, 1, MEMORY_LEAK, [start of procedure test1(),Condition is false,Condition is true,Condition is true,Condition is true] codetoanalyze/objc/shared/assertions/NSAssert_example.m, test2, 1, MEMORY_LEAK, [start of procedure test2(),Condition is false,Condition is true,Condition is true] codetoanalyze/objc/shared/block/BlockVar.m, BlockVar_blockPostBad, 5, NULL_DEREFERENCE, [start of procedure blockPostBad,start of procedure block,return from a call to objc_blockBlockVar_blockPostBad_2] codetoanalyze/objc/shared/block/BlockVar.m, BlockVar_capturedNullDeref, 5, NULL_DEREFERENCE, [start of procedure capturedNullDeref,start of procedure block]