[reporting] aggregate issues from all procedures before reporting

Summary:
The reporting phases iterates over each procedure summary and print all the issues from each procedure.
That's nice because we don't have to build a big list of the issues in-memory, but it's not so nice if you want to ouput the reports in a certain order or de-duplicate them.

This diff builds the in-memory list and outputs the issues afterward. By itself, this isn't very useful. But in the near future it will allow us to:
- Group all of the issues from the same file (finally!!!)
- Get rid of duplicate issues on multiple instantiations of the same C++ template
- Probably other cool stuff too

Reviewed By: jeremydubreil, mbouaziz

Differential Revision: D5816646

fbshipit-source-id: 799bcd0
master
Sam Blackshear 7 years ago committed by Facebook Github Bot
parent 59be79a81e
commit ca3e454481

@ -134,6 +134,11 @@ let iter (f: iter_fun) (err_log: t) =
(fun err_key set -> ErrDataSet.iter (fun err_data -> f err_key err_data) set) (fun err_key set -> ErrDataSet.iter (fun err_data -> f err_key err_data) set)
err_log err_log
let fold (f: err_key -> err_data -> 'a -> 'a) t acc =
ErrLogHash.fold
(fun err_key set acc -> ErrDataSet.fold (fun err_data acc -> f err_key err_data acc) set acc)
t acc
(** Return the number of elements in the error log which satisfy [filter] *) (** Return the number of elements in the error log which satisfy [filter] *)
let size filter (err_log: t) = let size filter (err_log: t) =
let count = ref 0 in let count = ref 0 in

@ -71,6 +71,8 @@ type iter_fun = err_key -> err_data -> unit
val iter : iter_fun -> t -> unit val iter : iter_fun -> t -> unit
(** Apply f to nodes and error names *) (** Apply f to nodes and error names *)
val fold : (err_key -> err_data -> 'a -> 'a) -> t -> 'a -> 'a
val pp_loc_trace_elem : Format.formatter -> loc_trace_elem -> unit val pp_loc_trace_elem : Format.formatter -> loc_trace_elem -> unit
val pp_loc_trace : Format.formatter -> loc_trace -> unit val pp_loc_trace : Format.formatter -> loc_trace -> unit

@ -244,10 +244,9 @@ module IssuesCsv = struct
Io_infer.Xml.tag_key Io_infer.Xml.tag_qualifier_tags Io_infer.Xml.tag_hash "bug_id" Io_infer.Xml.tag_key Io_infer.Xml.tag_qualifier_tags Io_infer.Xml.tag_hash "bug_id"
"always_report" "advice" "always_report" "advice"
(** Write bug report in csv format *) let pp_issue fmt error_filter procname proc_loc_opt (key: Errlog.err_key)
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt procname err_log = (err_data: Errlog.err_data) =
let pp x = F.fprintf fmt x in let pp x = F.fprintf fmt x in
let pp_row (key: Errlog.err_key) (err_data: Errlog.err_data) =
let source_file = let source_file =
match proc_loc_opt with match proc_loc_opt with
| Some proc_loc | Some proc_loc
@ -304,8 +303,10 @@ module IssuesCsv = struct
(* bug id *) (* bug id *)
pp "\"%s\"," always_report ; pp "\"%s\"," always_report ;
pp "\"%s\"@\n" err_advice_string pp "\"%s\"@\n" err_advice_string
in
Errlog.iter pp_row err_log (** Write bug report in csv format *)
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt procname err_log =
Errlog.iter (pp_issue fmt error_filter procname proc_loc_opt) err_log
end end
let potential_exception_message = "potential exception at line" let potential_exception_message = "potential exception at line"
@ -319,10 +320,9 @@ module IssuesJson = struct
let pp_json_close fmt () = F.fprintf fmt "]@\n@?" let pp_json_close fmt () = F.fprintf fmt "]@\n@?"
(** Write bug report in JSON format *) let pp_issue fmt error_filter procname proc_loc_opt (key: Errlog.err_key)
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt procname err_log = (err_data: Errlog.err_data) =
let pp x = F.fprintf fmt x in let pp x = F.fprintf fmt x in
let pp_row (key: Errlog.err_key) (err_data: Errlog.err_data) =
let source_file, procedure_start_line = let source_file, procedure_start_line =
match proc_loc_opt with match proc_loc_opt with
| Some proc_loc | Some proc_loc
@ -384,8 +384,7 @@ module IssuesJson = struct
; key= err_data.node_id_key.node_key ; key= err_data.node_id_key.node_key
; qualifier_tags= Localise.Tags.tag_value_records_of_tags key.err_desc.tags ; qualifier_tags= Localise.Tags.tag_value_records_of_tags key.err_desc.tags
; hash= ; hash=
get_bug_hash kind bug_type procedure_id file err_data.node_id_key.node_key get_bug_hash kind bug_type procedure_id file err_data.node_id_key.node_key key.err_desc
key.err_desc
; dotty= error_desc_to_dotty_string key.err_desc ; dotty= error_desc_to_dotty_string key.err_desc
; infer_source_loc= json_ml_loc ; infer_source_loc= json_ml_loc
; bug_type_hum= key.err_name.IssueType.hum ; bug_type_hum= key.err_name.IssueType.hum
@ -395,8 +394,10 @@ module IssuesJson = struct
in in
if not !is_first_item then pp "," else is_first_item := false ; if not !is_first_item then pp "," else is_first_item := false ;
pp "%s@?" (Jsonbug_j.string_of_jsonbug bug) pp "%s@?" (Jsonbug_j.string_of_jsonbug bug)
in
Errlog.iter pp_row err_log (** Write bug report in JSON format *)
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt procname err_log =
Errlog.iter (pp_issue fmt error_filter procname proc_loc_opt) err_log
end end
let pp_custom_of_report fmt report fields = let pp_custom_of_report fmt report fields =
@ -462,9 +463,7 @@ let tests_jsonbug_compare bug1 bug2 =
(bug2.file, bug2.procedure, bug2.line - bug2.procedure_start_line, bug2.bug_type, bug2.hash) (bug2.file, bug2.procedure, bug2.line - bug2.procedure_start_line, bug2.bug_type, bug2.hash)
module IssuesTxt = struct module IssuesTxt = struct
(** Write bug report in text format *) let pp_issue fmt error_filter proc_loc_opt (key: Errlog.err_key) (err_data: Errlog.err_data) =
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt _ err_log =
let pp_row (key: Errlog.err_key) (err_data: Errlog.err_data) =
let source_file = let source_file =
match proc_loc_opt with match proc_loc_opt with
| Some proc_loc | Some proc_loc
@ -475,8 +474,10 @@ module IssuesTxt = struct
if key.in_footprint && error_filter source_file key.err_desc key.err_name then if key.in_footprint && error_filter source_file key.err_desc key.err_name then
Exceptions.pp_err ~node_key:err_data.node_id_key.node_key err_data.loc key.err_kind Exceptions.pp_err ~node_key:err_data.node_id_key.node_key err_data.loc key.err_kind
key.err_name key.err_desc None fmt () key.err_name key.err_desc None fmt ()
in
Errlog.iter pp_row err_log (** Write bug report in text format *)
let pp_issues_of_error_log fmt error_filter _ proc_loc_opt _ err_log =
Errlog.iter (pp_issue fmt error_filter proc_loc_opt) err_log
end end
let pp_text_of_report fmt report = let pp_text_of_report fmt report =
@ -735,6 +736,20 @@ type report_kind = Issues | Procs | Stats | Calls | Summary [@@deriving compare]
type bug_format_kind = Json | Csv | Tests | Text | Latex [@@deriving compare] type bug_format_kind = Json | Csv | Tests | Text | Latex [@@deriving compare]
let pp_issue_in_format (format_kind, (outf: Utils.outfile)) error_filter
(procname, procname_loc, err_key, err_data) =
match format_kind with
| Csv
-> IssuesCsv.pp_issue outf.fmt error_filter procname (Some procname_loc) err_key err_data
| Json
-> IssuesJson.pp_issue outf.fmt error_filter procname (Some procname_loc) err_key err_data
| Latex
-> L.(die InternalError) "Printing issues in latex is not implemented"
| Tests
-> L.(die InternalError) "Print issues as tests is not implemented"
| Text
-> IssuesTxt.pp_issue outf.fmt error_filter (Some procname_loc) err_key err_data
let pp_issues_in_format (format_kind, (outf: Utils.outfile)) = let pp_issues_in_format (format_kind, (outf: Utils.outfile)) =
match format_kind with match format_kind with
| Json | Json
@ -782,11 +797,13 @@ let pp_issues_of_error_log error_filter linereader proc_loc_opt procname err_log
in in
List.iter ~f:pp_issues_in_format bug_format_list List.iter ~f:pp_issues_in_format bug_format_list
let pp_issues error_filter linereader summary bug_format_list = let collect_issues summary issues_acc =
let err_log = summary.Specs.attributes.ProcAttributes.err_log in let err_log = summary.Specs.attributes.ProcAttributes.err_log in
let procname = Specs.get_proc_name summary in let procname = Specs.get_proc_name summary in
let loc = summary.Specs.attributes.ProcAttributes.loc in let proc_loc = summary.Specs.attributes.ProcAttributes.loc in
pp_issues_of_error_log error_filter linereader (Some loc) procname err_log bug_format_list Errlog.fold
(fun err_key err_data acc -> (procname, proc_loc, err_key, err_data) :: acc)
err_log issues_acc
let pp_procs summary procs_format_list = let pp_procs summary procs_format_list =
let pp_procs_in_format format = let pp_procs_in_format format =
@ -820,11 +837,9 @@ let pp_summary summary fname summary_format_list =
Summary.print_summary_dot_svg summary fname Summary.print_summary_dot_svg summary fname
let pp_summary_by_report_kind formats_by_report_kind summary fname error_filter linereader stats let pp_summary_by_report_kind formats_by_report_kind summary fname error_filter linereader stats
file = file issues_acc =
let pp_summary_by_report_kind (report_kind, format_list) = let pp_summary_by_report_kind (report_kind, format_list) =
match (report_kind, format_list) with match (report_kind, format_list) with
| Issues, _ :: _
-> pp_issues error_filter linereader summary format_list
| Procs, _ :: _ | Procs, _ :: _
-> pp_procs summary format_list -> pp_procs summary format_list
| Stats, _ :: _ | Stats, _ :: _
@ -836,7 +851,7 @@ let pp_summary_by_report_kind formats_by_report_kind summary fname error_filter
| _ | _
-> () -> ()
in in
List.iter ~f:pp_summary_by_report_kind formats_by_report_kind List.iter ~f:pp_summary_by_report_kind formats_by_report_kind ; collect_issues summary issues_acc
let pp_json_report_by_report_kind formats_by_report_kind fname = let pp_json_report_by_report_kind formats_by_report_kind fname =
match Utils.read_file fname with match Utils.read_file fname with
@ -888,15 +903,19 @@ let pp_lint_issues filters formats_by_report_kind linereader procname error_log
pp_lint_issues_by_report_kind formats_by_report_kind error_filter linereader procname error_log pp_lint_issues_by_report_kind formats_by_report_kind error_filter linereader procname error_log
(** Process a summary *) (** Process a summary *)
let process_summary filters formats_by_report_kind linereader stats (fname, summary) = let process_summary filters formats_by_report_kind linereader stats fname summary issues_acc =
let file = summary.Specs.attributes.ProcAttributes.loc.Location.file in let file = summary.Specs.attributes.ProcAttributes.loc.Location.file in
let proc_name = Specs.get_proc_name summary in let proc_name = Specs.get_proc_name summary in
let error_filter = error_filter filters proc_name in let error_filter = error_filter filters proc_name in
let pp_simple_saved = !Config.pp_simple in let pp_simple_saved = !Config.pp_simple in
Config.pp_simple := true ; Config.pp_simple := true ;
pp_summary_by_report_kind formats_by_report_kind summary fname error_filter linereader stats file ; let issues_acc' =
pp_summary_by_report_kind formats_by_report_kind summary fname error_filter linereader stats
file issues_acc
in
if Config.precondition_stats then PreconditionStats.do_summary proc_name summary ; if Config.precondition_stats then PreconditionStats.do_summary proc_name summary ;
Config.pp_simple := pp_simple_saved Config.pp_simple := pp_simple_saved ;
issues_acc'
module AnalysisResults = struct module AnalysisResults = struct
type t = (string * Specs.summary) list type t = (string * Specs.summary) list
@ -1070,33 +1089,43 @@ let finalize_and_close_files format_list_by_kind stats pdflatex =
in in
List.iter ~f:close_files_of_report_kind format_list_by_kind List.iter ~f:close_files_of_report_kind format_list_by_kind
let pp_summary_and_issues formats_by_report_kind = let pp_summary_and_issues formats_by_report_kind issue_formats =
let pdflatex fname = ignore (Sys.command ("pdflatex " ^ fname)) in let pdflatex fname = ignore (Sys.command ("pdflatex " ^ fname)) in
let stats = Stats.create () in let stats = Stats.create () in
let linereader = Printer.LineReader.create () in let linereader = Printer.LineReader.create () in
let filters = Inferconfig.create_filters Config.analyzer in let filters = Inferconfig.create_filters Config.analyzer in
let iterate_summaries = AnalysisResults.get_summary_iterator () in let iterate_summaries = AnalysisResults.get_summary_iterator () in
iterate_summaries (process_summary filters formats_by_report_kind linereader stats) ; let all_issues = ref [] in
iterate_summaries (fun (filename, summary) ->
all_issues
:= process_summary filters formats_by_report_kind linereader stats filename summary
!all_issues ) ;
List.iter
~f:(fun (procname, _, _, _ as issue) ->
let error_filter = error_filter filters procname in
List.iter
~f:(fun issue_format -> pp_issue_in_format issue_format error_filter issue)
issue_formats)
(List.rev !all_issues) ;
if Config.precondition_stats then PreconditionStats.pp_stats () ; if Config.precondition_stats then PreconditionStats.pp_stats () ;
LintIssues.load_issues_to_errlog_map Config.lint_issues_dir_name ; LintIssues.load_issues_to_errlog_map Config.lint_issues_dir_name ;
Typ.Procname.Map.iter (pp_lint_issues filters formats_by_report_kind linereader) Typ.Procname.Map.iter (pp_lint_issues filters formats_by_report_kind linereader)
!LintIssues.errLogMap ; !LintIssues.errLogMap ;
finalize_and_close_files formats_by_report_kind stats pdflatex finalize_and_close_files formats_by_report_kind stats pdflatex
let print_issues formats_by_report_kind =
init_files formats_by_report_kind ;
match Config.from_json_report with
| Some fname
-> pp_json_report_by_report_kind formats_by_report_kind fname
| None
-> pp_summary_and_issues formats_by_report_kind
let main ~report_csv ~report_json = let main ~report_csv ~report_json =
let issue_formats = init_issues_format_list report_csv report_json in
let formats_by_report_kind = let formats_by_report_kind =
[ (Issues, init_issues_format_list report_csv report_json) [ (Issues, issue_formats)
; (Procs, init_procs_format_list ()) ; (Procs, init_procs_format_list ())
; (Calls, init_calls_format_list ()) ; (Calls, init_calls_format_list ())
; (Stats, init_stats_format_list ()) ; (Stats, init_stats_format_list ())
; (Summary, init_summary_format_list ()) ] ; (Summary, init_summary_format_list ()) ]
in in
register_perf_stats_report () ; print_issues formats_by_report_kind register_perf_stats_report () ;
init_files formats_by_report_kind ;
match Config.from_json_report with
| Some fname
-> pp_json_report_by_report_kind formats_by_report_kind fname
| None
-> pp_summary_and_issues formats_by_report_kind issue_formats

Loading…
Cancel
Save