From 1c375a17ac20001fc64efe23b8199338a3c7731f Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Wed, 30 Aug 2017 06:59:39 -0700 Subject: [PATCH] [log] die more appropriately Summary: - failwith police: no more `failwith`. Instead, use `Logging.die`. - Introduce the `SimpleLogging` module for dying from modules where `Logging` cannot be used (usually because that would create a cyclic dependency). - always log backtraces, and show backtraces on the console except for usage errors - Also point out in the log file where the toplevel executions of infer happen Reviewed By: jeremydubreil Differential Revision: D5726362 fbshipit-source-id: d7a01fc --- infer/src/IR/Cfg.ml | 2 +- infer/src/IR/HilExp.ml | 2 +- infer/src/IR/HilInstr.ml | 8 +- infer/src/IR/IntLit.ml | 5 +- infer/src/IR/Pvar.ml | 2 +- infer/src/IR/QualifiedCppName.ml | 7 +- infer/src/absint/AbstractInterpreter.ml | 2 +- infer/src/backend/DifferentialFilters.ml | 5 +- infer/src/backend/InferPrint.ml | 23 +++--- infer/src/backend/StatsAggregator.ml | 25 +++--- infer/src/backend/builtin.ml | 3 +- infer/src/backend/callbacks.ml | 2 +- infer/src/backend/crashcontext.ml | 2 +- infer/src/backend/exe_env.ml | 9 ++- infer/src/backend/infer.ml | 3 +- infer/src/backend/inferconfig.ml | 10 ++- infer/src/backend/ondemand.ml | 10 ++- infer/src/backend/preanal.ml | 7 +- infer/src/backend/prover.ml | 5 +- infer/src/backend/reporting.ml | 3 +- infer/src/backend/specs.ml | 3 +- infer/src/backend/symExec.ml | 7 +- infer/src/base/CommandLineOption.ml | 14 ++-- infer/src/base/CommandLineOption.mli | 2 +- infer/src/base/Config.ml | 49 ++++++++---- infer/src/base/DB.ml | 8 +- infer/src/base/Logging.ml | 80 +++++++++---------- infer/src/base/Logging.mli | 14 ++-- infer/src/base/SimpleLogging.ml | 41 ++++++++++ infer/src/base/SimpleLogging.mli | 26 ++++++ infer/src/base/SourceFile.ml | 8 +- infer/src/base/Utils.ml | 5 +- .../src/bufferoverrun/bufferOverrunChecker.ml | 4 +- infer/src/bufferoverrun/itv.ml | 12 +-- infer/src/checkers/BoundedCallTree.ml | 4 +- infer/src/checkers/IdAccessPathMapDomain.ml | 4 +- infer/src/checkers/NullabilitySuggest.ml | 2 +- infer/src/checkers/SimpleChecker.ml | 5 +- infer/src/checkers/SinkTrace.ml | 2 +- infer/src/checkers/Siof.ml | 2 +- infer/src/checkers/Stacktrace.ml | 8 +- infer/src/checkers/ThreadSafety.ml | 17 ++-- infer/src/checkers/ThreadSafetyConfig.ml | 4 +- infer/src/checkers/annotationReachability.ml | 3 +- infer/src/checkers/printfArgs.ml | 2 +- infer/src/clang/ClangPointers.ml | 5 +- infer/src/clang/ClangWrapper.ml | 4 +- infer/src/clang/cAst_utils.ml | 2 +- infer/src/clang/cFrontend_checkers.ml | 24 +++--- infer/src/clang/cFrontend_checkers_main.ml | 2 +- infer/src/clang/cFrontend_errors.ml | 17 ++-- infer/src/clang/cPredicates.ml | 6 +- infer/src/clang/cTL.ml | 6 +- infer/src/clang/cType_to_sil_type.ml | 2 +- infer/src/clang/clang_ast_extend.ml | 48 +++++------ infer/src/clang/ctl_parser.mly | 11 +-- infer/src/harness/inhabit.ml | 5 +- infer/src/integration/Buck.ml | 6 +- .../integration/CaptureCompilationDatabase.ml | 7 +- infer/src/integration/Clang.ml | 3 +- infer/src/integration/CompilationDatabase.ml | 2 +- infer/src/integration/Driver.ml | 4 +- infer/src/integration/Javac.ml | 2 +- infer/src/integration/Maven.ml | 7 +- infer/src/istd/IStd.ml | 10 ++- infer/src/java/jClasspath.ml | 8 +- infer/src/java/jFrontend.ml | 2 +- infer/src/java/jMain.ml | 9 ++- infer/src/java/jTrans.ml | 3 +- infer/src/labs/ResourceLeaks.ml | 5 +- infer/src/quandary/ClangTrace.ml | 4 +- infer/src/quandary/JavaTaintAnalysis.ml | 2 +- infer/src/quandary/JavaTrace.ml | 8 +- infer/src/quandary/TaintAnalysis.ml | 15 ++-- infer/src/unit/DifferentialFiltersTests.ml | 13 +-- infer/src/unit/UnitUtils.ml | 23 ++++++ infer/src/unit/UnitUtils.mli | 14 ++++ infer/src/unit/stacktraceTests.ml | 3 +- 78 files changed, 449 insertions(+), 294 deletions(-) create mode 100644 infer/src/base/SimpleLogging.ml create mode 100644 infer/src/base/SimpleLogging.mli create mode 100644 infer/src/unit/UnitUtils.ml create mode 100644 infer/src/unit/UnitUtils.mli diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index 2a571d2f5..b196cc870 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -84,7 +84,7 @@ let check_cfg_connectedness cfg = let nodes = Procdesc.get_nodes pd in (* TODO (T20302015): also check the CFGs for the C-like procedures *) if not Config.keep_going && Typ.Procname.is_java pname && List.exists ~f:broken_node nodes then - failwithf "Broken CFG on %a" Typ.Procname.pp pname + L.(die InternalError) "Broken CFG on %a" Typ.Procname.pp pname in let pdescs = get_all_procs cfg in List.iter ~f:do_pdesc pdescs diff --git a/infer/src/IR/HilExp.ml b/infer/src/IR/HilExp.ml index a1ad95129..fa559c9af 100644 --- a/infer/src/IR/HilExp.ml +++ b/infer/src/IR/HilExp.ml @@ -162,7 +162,7 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ = | Some access_path -> AccessPath access_path | None - -> failwithf "Couldn't convert var expression %a to access path" Exp.pp exp + -> L.(die InternalError) "Couldn't convert var expression %a to access path" Exp.pp exp in of_sil_ exp typ diff --git a/infer/src/IR/HilInstr.ml b/infer/src/IR/HilInstr.ml index fbf163f91..c4a33cfb6 100644 --- a/infer/src/IR/HilInstr.ml +++ b/infer/src/IR/HilInstr.ml @@ -84,10 +84,10 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) = | ap :: _ -> ap | [] - -> invalid_argf "Invalid pointer arithmetic expression %a used as LHS" Exp.pp lhs_exp - ) + -> L.(die InternalError) + "Invalid pointer arithmetic expression %a used as LHS" Exp.pp lhs_exp ) | _ - -> invalid_argf "Non-assignable LHS expression %a" Exp.pp lhs_exp + -> L.(die InternalError) "Non-assignable LHS expression %a" Exp.pp lhs_exp in Instr (Assign (lhs_access_path, exp_of_sil rhs_exp typ, loc)) | Call (ret_opt, call_exp, formals, loc, call_flags) @@ -99,7 +99,7 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) = | AccessPath access_path -> Indirect access_path | call_exp - -> invalid_argf "Unexpected call expression %a" HilExp.pp call_exp + -> L.(die InternalError) "Unexpected call expression %a" HilExp.pp call_exp in let formals = List.map ~f:(fun (exp, typ) -> exp_of_sil exp typ) formals in Instr (Call (hil_ret, hil_call, formals, call_flags, loc)) diff --git a/infer/src/IR/IntLit.ml b/infer/src/IR/IntLit.ml index ea632309e..81942037b 100644 --- a/infer/src/IR/IntLit.ml +++ b/infer/src/IR/IntLit.ml @@ -9,6 +9,7 @@ *) open! IStd module F = Format +module L = Logging (** signed and unsigned integer literals *) type t = bool * Int64.t * bool @@ -108,7 +109,7 @@ let sub i1 i2 = add i1 (neg i2) let shift_left (unsigned1, i1, ptr1) (_, i2, _) = match Int64.to_int i2 with | None - -> failwithf "Shifting failed with operand %a" Int64.pp i2 + -> L.(die InternalError) "Shifting failed with operand %a" Int64.pp i2 | Some i2 -> if i2 < 0 || i2 >= 64 then raise OversizedShift ; let res = Int64.shift_left i1 i2 in @@ -117,7 +118,7 @@ let shift_left (unsigned1, i1, ptr1) (_, i2, _) = let shift_right (unsigned1, i1, ptr1) (_, i2, _) = match Int64.to_int i2 with | None - -> failwithf "Shifting failed with operand %a" Int64.pp i2 + -> L.(die InternalError) "Shifting failed with operand %a" Int64.pp i2 | Some i2 -> if i2 < 0 || i2 >= 64 then raise OversizedShift ; let res = Int64.shift_right i1 i2 in diff --git a/infer/src/IR/Pvar.ml b/infer/src/IR/Pvar.ml index 40c9e2522..ff9a06528 100644 --- a/infer/src/IR/Pvar.ml +++ b/infer/src/IR/Pvar.ml @@ -227,7 +227,7 @@ let get_translation_unit pvar = | Global_var (tu, _, _, _) -> tu | _ - -> invalid_argf "Expected a global variable" + -> L.(die InternalError) "Expected a global variable" let is_compile_constant pvar = match pvar.pv_kind with Global_var (_, b, _, _) -> b | _ -> false diff --git a/infer/src/IR/QualifiedCppName.ml b/infer/src/IR/QualifiedCppName.ml index e40b323bf..5180dc459 100644 --- a/infer/src/IR/QualifiedCppName.ml +++ b/infer/src/IR/QualifiedCppName.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging (* internally it uses reversed list to store qualified name, for example: ["get", "shared_ptr", "std"]*) type t = string list [@@deriving compare] @@ -27,13 +28,13 @@ let strip_template_args quals = let append_template_args_to_last quals ~args = match quals with | [last; _] when String.contains last '<' - -> failwithf + -> L.(die InternalError) "expected qualified name without template args, but got %s, the last qualifier of %s" last (String.concat ~sep:", " quals) | last :: rest -> (last ^ args) :: rest | [] - -> failwith "expected non-empty qualified name" + -> L.(die InternalError) "expected non-empty qualified name" let to_list = List.rev @@ -82,7 +83,7 @@ module Match = struct List.iter colon_splits ~f:(fun s -> (* Filter out the '<' in operator< and operator<= *) if not (String.is_prefix s ~prefix:"operator<") && String.contains s '<' then - failwithf "Unexpected template in fuzzy qualified name %s." qual_name ) ; + L.(die InternalError) "Unexpected template in fuzzy qualified name %s." qual_name ) ; of_qual_string qual_name let of_fuzzy_qual_names fuzzy_qual_names = diff --git a/infer/src/absint/AbstractInterpreter.ml b/infer/src/absint/AbstractInterpreter.ml index 255fc1acf..3a92a535e 100644 --- a/infer/src/absint/AbstractInterpreter.ml +++ b/infer/src/absint/AbstractInterpreter.ml @@ -99,7 +99,7 @@ struct else let visit_count' = old_state.visit_count + 1 in if visit_count' > Config.max_widens then - failwithf + L.(die InternalError) "Exceeded max widening threshold %d while analyzing %a. Please check your widening operator or increase the threshold" Config.max_widens Typ.Procname.pp (Procdesc.get_proc_name pdesc) ; update_inv_map widened_pre visit_count' diff --git a/infer/src/backend/DifferentialFilters.ml b/infer/src/backend/DifferentialFilters.ml index f9d022750..1419de1e1 100644 --- a/infer/src/backend/DifferentialFilters.ml +++ b/infer/src/backend/DifferentialFilters.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging module FileRenamings = struct type renaming = {current: string; previous: string} [@@deriving compare] @@ -45,7 +46,7 @@ module FileRenamings = struct | _ -> raise (Yojson.Json_error "not a record") with Yojson.Json_error err -> - failwithf + L.(die UserError) "Error parsing file renamings: %s@\nExpected JSON object of the following form: '%s', but instead got: '%s'" err "{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}" (Yojson.Basic.to_string assoc) @@ -54,7 +55,7 @@ module FileRenamings = struct | `List json_renamings -> List.map ~f:renaming_of_assoc json_renamings | _ - -> failwithf "Expected JSON list but got '%s'" input + -> L.(die UserError) "Expected JSON list but got '%s'" input let from_json_file file : t = from_json (In_channel.read_all file) diff --git a/infer/src/backend/InferPrint.ml b/infer/src/backend/InferPrint.ml index 3be731e2b..27ef7da86 100644 --- a/infer/src/backend/InferPrint.ml +++ b/infer/src/backend/InferPrint.ml @@ -331,7 +331,8 @@ module IssuesJson = struct -> (err_data.loc.Location.file, 0) in if SourceFile.is_invalid source_file then - failwithf "Invalid source file for %a %a@.Trace: %a@." IssueType.pp key.err_name + L.(die InternalError) + "Invalid source file for %a %a@.Trace: %a@." IssueType.pp key.err_name Localise.pp_error_desc key.err_desc Errlog.pp_loc_trace err_data.loc_trace ; let should_report_source_file = not (SourceFile.is_infer_model source_file) || Config.debug_mode || Config.debug_exceptions @@ -741,39 +742,39 @@ let pp_issues_in_format (format_kind, (outf: Utils.outfile)) = | Csv -> IssuesCsv.pp_issues_of_error_log outf.fmt | Tests - -> failwith "Print issues as tests is not implemented" + -> L.(die InternalError) "Print issues as tests is not implemented" | Text -> IssuesTxt.pp_issues_of_error_log outf.fmt | Latex - -> failwith "Printing issues in latex is not implemented" + -> L.(die InternalError) "Printing issues in latex is not implemented" let pp_procs_in_format (format_kind, (outf: Utils.outfile)) = match format_kind with | Csv -> ProcsCsv.pp_summary outf.fmt | Json | Latex | Tests | Text - -> failwith "Printing procs in json/latex/tests/text is not implemented" + -> L.(die InternalError) "Printing procs in json/latex/tests/text is not implemented" let pp_calls_in_format (format_kind, (outf: Utils.outfile)) = match format_kind with | Csv -> CallsCsv.pp_calls outf.fmt | Json | Tests | Text | Latex - -> failwith "Printing calls in json/tests/text/latex is not implemented" + -> L.(die InternalError) "Printing calls in json/tests/text/latex is not implemented" let pp_stats_in_format (format_kind, _) = match format_kind with | Csv -> Stats.process_summary | Json | Tests | Text | Latex - -> failwith "Printing stats in json/tests/text/latex is not implemented" + -> L.(die InternalError) "Printing stats in json/tests/text/latex is not implemented" let pp_summary_in_format (format_kind, (outf: Utils.outfile)) = match format_kind with | Latex -> Summary.write_summary_latex outf.fmt | Json | Csv | Tests | Text - -> failwith "Printing summary in json/csv/tests/text is not implemented" + -> L.(die InternalError) "Printing summary in json/csv/tests/text is not implemented" let pp_issues_of_error_log error_filter linereader proc_loc_opt procname err_log bug_format_list = let pp_issues_in_format format = @@ -848,11 +849,11 @@ let pp_json_report_by_report_kind formats_by_report_kind fname = | Text -> pp_text_of_report outf.fmt report | Json - -> failwith "Printing issues from json does not support json output" + -> L.(die InternalError) "Printing issues from json does not support json output" | Csv - -> failwith "Printing issues from json does not support csv output" + -> L.(die InternalError) "Printing issues from json does not support csv output" | Latex - -> failwith "Printing issues from json does not support latex output" + -> L.(die InternalError) "Printing issues from json does not support latex output" in List.iter ~f:pp_json_issue format_list in @@ -869,7 +870,7 @@ let pp_json_report_by_report_kind formats_by_report_kind fname = in List.iter ~f:pp_report_by_report_kind formats_by_report_kind | Error error - -> failwithf "Error reading '%s': %s" fname error + -> L.(die UserError) "Error reading '%s': %s" fname error let pp_lint_issues_by_report_kind formats_by_report_kind error_filter linereader procname error_log = let pp_summary_by_report_kind (report_kind, format_list) = diff --git a/infer/src/backend/StatsAggregator.ml b/infer/src/backend/StatsAggregator.ml index dd0776be6..17de41ad5 100644 --- a/infer/src/backend/StatsAggregator.ml +++ b/infer/src/backend/StatsAggregator.ml @@ -8,6 +8,7 @@ *) open! IStd open! PVariant +module L = Logging let aggregated_stats_filename = "aggregated_stats.json" @@ -51,22 +52,18 @@ let find_stats_files_in_dir dir = {frontend_paths; backend_paths; reporting_paths} let load_data_from_infer_deps file = + let error msg = Printf.sprintf ("Error reading '%s': " ^^ msg) file in let extract_target_and_path line = - match Str.split_delim (Str.regexp (Str.quote "\t")) line with + match String.split ~on:'\t' line with | target :: _ :: path :: _ - -> if dir_exists path then (target, path) - else raise (Failure ("path '" ^ path ^ "' is not a valid directory")) + -> if dir_exists path then Ok (target, path) + else Error (error "path '%s' is not a valid directory" path) | _ - -> raise (Failure "malformed input") + -> Error (error "malformed input") in - let lines = Utils.read_file file in - try - match lines with - | Ok l - -> Ok (List.map ~f:extract_target_and_path l) - | Error error - -> raise (Failure (Printf.sprintf "Error reading '%s': %s" file error)) - with Failure msg -> Error msg + let parse_lines lines = List.map lines ~f:extract_target_and_path |> Result.all in + Utils.read_file file |> Result.map_error ~f:(fun msg -> error "%s" msg) + |> Result.bind ~f:parse_lines let collect_all_stats_files () = let infer_out = Config.results_dir in @@ -138,7 +135,9 @@ let aggregate_stats_by_target tp = let generate_files () = let infer_out = Config.results_dir in let stats_files = collect_all_stats_files () in - let origin = match stats_files with Ok origin -> origin | Error e -> failwith e in + let origin = + match stats_files with Ok origin -> origin | Error e -> L.(die InternalError) "%s" e + in let aggregated_frontend_stats_dir = Filename.concat infer_out Config.frontend_stats_dir_name in let aggregated_backend_stats_dir = Filename.concat infer_out Config.backend_stats_dir_name in let aggregated_reporting_stats_dir = Filename.concat infer_out Config.reporting_stats_dir_name in diff --git a/infer/src/backend/builtin.ml b/infer/src/backend/builtin.ml index 8fd9efa10..dc184854f 100644 --- a/infer/src/backend/builtin.ml +++ b/infer/src/backend/builtin.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging (** Module for builtin functions with their symbolic execution handler *) @@ -34,7 +35,7 @@ let builtin_functions = Typ.Procname.Hash.create 4 let check_register_populated () = (* check if BuiltinDefn were loaded before accessing register *) if Int.equal (Typ.Procname.Hash.length builtin_functions) 0 then - failwith "Builtins were not initialized" + L.(die InternalError) "Builtins were not initialized" (** check if the function is a builtin *) let is_registered name = diff --git a/infer/src/backend/callbacks.ml b/infer/src/backend/callbacks.ml index 9ccf78923..f16fd404c 100644 --- a/infer/src/backend/callbacks.ml +++ b/infer/src/backend/callbacks.ml @@ -126,7 +126,7 @@ let iterate_callbacks call_graph exe_env = let analyze_proc_name pname = match Ondemand.get_proc_desc pname with | None - -> failwithf "Could not find proc desc for %a" Typ.Procname.pp pname + -> L.(die InternalError) "Could not find proc desc for %a" Typ.Procname.pp pname | Some pdesc -> ignore (Ondemand.analyze_proc_desc pdesc pdesc) in diff --git a/infer/src/backend/crashcontext.ml b/infer/src/backend/crashcontext.ml index 3d885e6e8..880fc85a7 100644 --- a/infer/src/backend/crashcontext.ml +++ b/infer/src/backend/crashcontext.ml @@ -25,7 +25,7 @@ let frame_id_of_summary stacktree = let short_name = List.hd_exn (Str.split (Str.regexp "(") stacktree.Stacktree_j.method_name) in match stacktree.Stacktree_j.location with | None - -> failwith + -> L.(die InternalError) "Attempted to take signature of a frame without location information. This is undefined." | Some {line= Some line_num; file} -> F.sprintf "%s(%s:%d)" short_name (Filename.basename file) line_num diff --git a/infer/src/backend/exe_env.ml b/infer/src/backend/exe_env.ml index 39fb1b847..08dc6cd83 100644 --- a/infer/src/backend/exe_env.ml +++ b/infer/src/backend/exe_env.ml @@ -149,8 +149,8 @@ let java_global_tenv = ( lazy ( match Tenv.load_from_file DB.global_tenv_fname with | None - -> failwithf "Could not load the global tenv at path %s@." - (DB.filename_to_string DB.global_tenv_fname) + -> L.(die InternalError) + "Could not load the global tenv at path '%s'" (DB.filename_to_string DB.global_tenv_fname) | Some tenv -> tenv ) ) @@ -166,10 +166,11 @@ let get_tenv exe_env proc_name = | Some tenv -> tenv | None - -> failwithf "get_tenv: tenv not found for %a in file %s" Typ.Procname.pp proc_name + -> L.(die InternalError) + "get_tenv: tenv not found for %a in file '%s'" Typ.Procname.pp proc_name (DB.filename_to_string file_data.tenv_file) ) | None - -> failwithf "get_tenv: file_data not found for %a" Typ.Procname.pp proc_name + -> L.(die InternalError) "get_tenv: file_data not found for %a" Typ.Procname.pp proc_name (** return the cfg associated to the procedure *) let get_cfg exe_env pname = diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index 0cc500fca..85c96ac8a 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -118,7 +118,8 @@ let () = -> (* at least one report must be passed in input to compute differential *) ( match (Config.report_current, Config.report_previous) with | None, None - -> failwith "Expected at least one argument among 'report-current' and 'report-previous'\n" + -> L.(die UserError) + "Expected at least one argument among 'report-current' and 'report-previous'" | _ -> () ) ; ReportDiff.reportdiff ~current_report:Config.report_current diff --git a/infer/src/backend/inferconfig.ml b/infer/src/backend/inferconfig.ml index 4fc26cdcf..6aa6a11bf 100644 --- a/infer/src/backend/inferconfig.ml +++ b/infer/src/backend/inferconfig.ml @@ -178,7 +178,7 @@ module OverridesMatcher = struct -> is_subtype mp.class_name && Option.value_map ~f:(match_method language proc_name) ~default:false mp.method_name | _ - -> failwith "Expecting method pattern" + -> L.(die UserError) "Expecting method pattern" in List.exists ~f:is_matching patterns end @@ -229,7 +229,9 @@ let patterns_of_json_with_key (json_key, json) = | `String s -> s :: accu | _ - -> failwith ("Unrecognised parameters in " ^ Yojson.Basic.to_string (`Assoc assoc)) + -> L.(die UserError) + "Unrecognised parameters in %s" + (Yojson.Basic.to_string (`Assoc assoc)) in List.rev (List.fold ~f:collect ~init:[] l) in @@ -244,7 +246,7 @@ let patterns_of_json_with_key (json_key, json) = | key, _ when String.equal key "language" -> mp | _ - -> failwith ("Fails to parse " ^ Yojson.Basic.to_string (`Assoc assoc)) + -> L.(die UserError) "Failed to parse %s" (Yojson.Basic.to_string (`Assoc assoc)) in List.fold ~f:loop ~init:default_method_pattern assoc and create_string_contains assoc = @@ -254,7 +256,7 @@ let patterns_of_json_with_key (json_key, json) = | key, _ when String.equal key "language" -> sc | _ - -> failwith ("Fails to parse " ^ Yojson.Basic.to_string (`Assoc assoc)) + -> L.(die UserError) "Failed to parse %s" (Yojson.Basic.to_string (`Assoc assoc)) in List.fold ~f:loop ~init:default_source_contains assoc in diff --git a/infer/src/backend/ondemand.ml b/infer/src/backend/ondemand.ml index d32f154bd..1d48e0f57 100644 --- a/infer/src/backend/ondemand.ml +++ b/infer/src/backend/ondemand.ml @@ -157,8 +157,9 @@ let analyze_proc_desc curr_pdesc callee_pdesc : Specs.summary option = let proc_attributes = Procdesc.get_attributes callee_pdesc in match !callbacks_ref with | None - -> failwithf "No callbacks registered to analyze proc desc %a when analyzing %a@." - Typ.Procname.pp callee_pname Typ.Procname.pp (Procdesc.get_proc_name curr_pdesc) + -> L.(die InternalError) + "No callbacks registered to analyze proc desc %a when analyzing %a@." Typ.Procname.pp + callee_pname Typ.Procname.pp (Procdesc.get_proc_name curr_pdesc) | Some callbacks -> if should_be_analyzed callee_pname proc_attributes then Some (run_proc_analysis callbacks.analyze_ondemand curr_pdesc callee_pdesc) @@ -170,8 +171,9 @@ let analyze_proc_desc curr_pdesc callee_pdesc : Specs.summary option = let analyze_proc_name curr_pdesc callee_pname : Specs.summary option = match !callbacks_ref with | None - -> failwithf "No callbacks registered to analyze proc name %a when analyzing %a@." - Typ.Procname.pp callee_pname Typ.Procname.pp (Procdesc.get_proc_name curr_pdesc) + -> L.(die InternalError) + "No callbacks registered to analyze proc name %a when analyzing %a@." Typ.Procname.pp + callee_pname Typ.Procname.pp (Procdesc.get_proc_name curr_pdesc) | Some callbacks -> if procedure_should_be_analyzed callee_pname then match callbacks.get_proc_desc callee_pname with diff --git a/infer/src/backend/preanal.ml b/infer/src/backend/preanal.ml index a2579214c..561e5032f 100644 --- a/infer/src/backend/preanal.ml +++ b/infer/src/backend/preanal.ml @@ -10,6 +10,7 @@ open! IStd open! PVariant +module L = Logging (** mutate the cfg/cg to add dynamic dispatch handling *) let add_dispatch_calls pdesc cg tenv = @@ -160,7 +161,8 @@ module NullifyTransferFunctions = struct | Sil.Store _ | Prune _ | Declare_locals _ | Remove_temps _ | Abstract _ -> astate | Sil.Nullify _ - -> failwith "Should not add nullify instructions before running nullify analysis!" + -> L.(die InternalError) + "Should not add nullify instructions before running nullify analysis!" in if is_last_instr_in_node instr node then postprocess astate' node extras else astate' end @@ -236,8 +238,7 @@ let do_liveness pdesc tenv = LivenessAnalysis.exec_cfg liveness_proc_cfg (ProcData.make_default pdesc tenv) ~initial ~debug:false in - add_nullify_instrs pdesc tenv liveness_inv_map ; - Procdesc.signal_did_preanalysis pdesc + add_nullify_instrs pdesc tenv liveness_inv_map ; Procdesc.signal_did_preanalysis pdesc let do_abstraction pdesc = add_abstraction_instructions pdesc ; Procdesc.signal_did_preanalysis pdesc diff --git a/infer/src/backend/prover.ml b/infer/src/backend/prover.ml index 6f548fe82..0d703808c 100644 --- a/infer/src/backend/prover.ml +++ b/infer/src/backend/prover.ml @@ -1734,7 +1734,8 @@ let expand_hpred_pointer = Exp.Sizeof {sizeof_data with typ= Typ.mk (Tstruct name)} | _ -> (* type of struct at adr_base and of contents are both unknown: give up *) - raise (Failure "expand_hpred_pointer: Unexpected non-sizeof type in Lfield") + L.(die InternalError) + "expand_hpred_pointer: Unexpected non-sizeof type in Lfield" in let hpred' = Sil.Hpointsto (adr_base, Estruct ([(fld, cnt)], Sil.inst_none), cnt_texp') @@ -1746,7 +1747,7 @@ let expand_hpred_pointer = | Exp.Sizeof ({typ= t_} as sizeof_data) -> Exp.Sizeof {sizeof_data with typ= Typ.mk (Tarray (t_, None, None))} | _ - -> raise (Failure "expand_hpred_pointer: Unexpected non-sizeof type in Lindex") + -> L.(die InternalError) "expand_hpred_pointer: Unexpected non-sizeof type in Lindex" in let len = match t' with diff --git a/infer/src/backend/reporting.ml b/infer/src/backend/reporting.ml index b1dbe6642..06b870ba7 100644 --- a/infer/src/backend/reporting.ml +++ b/infer/src/backend/reporting.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging type log_t = ?loc:Location.t -> ?node_id:int * int -> ?session:int -> ?ltr:Errlog.loc_trace @@ -56,7 +57,7 @@ let log_issue_deprecated ?(store_summary= false) err_kind proc_name ?loc ?node_i (* TODO (#16348004): This is currently needed as ThreadSafety works as a cluster checker *) Specs.store_summary summary | None - -> failwithf + -> L.(die InternalError) "Trying to report error on procedure %a, but cannot because no summary exists for this procedure. Did you mean to log the error on the caller of %a instead?" Typ.Procname.pp proc_name Typ.Procname.pp proc_name diff --git a/infer/src/backend/specs.ml b/infer/src/backend/specs.ml index d648d84f5..e72765a6b 100644 --- a/infer/src/backend/specs.ml +++ b/infer/src/backend/specs.ml @@ -607,7 +607,8 @@ let rec get_summary proc_name = let get_summary_unsafe s proc_name = match get_summary proc_name with | None - -> failwithf "[%s] Specs.get_summary_unsafe: %a Not found" s Typ.Procname.pp proc_name + -> L.(die InternalError) + "[%s] Specs.get_summary_unsafe: %a Not found" s Typ.Procname.pp proc_name | Some summary -> summary diff --git a/infer/src/backend/symExec.ml b/infer/src/backend/symExec.ml index 374624b12..510d6b9f6 100644 --- a/infer/src/backend/symExec.ml +++ b/infer/src/backend/symExec.ml @@ -654,7 +654,7 @@ let resolve_virtual_pname tenv prop actuals callee_pname call_flags : Typ.Procna -> (* default mode for Java virtual calls: resolution only *) [resolved_target] ) | _ - -> failwith "A virtual call must have a receiver" + -> L.(die InternalError) "A virtual call must have a receiver" (** Resolve the name of the procedure to call based on the type of the arguments *) let resolve_java_pname tenv prop args pname_java call_flags : Typ.Procname.java = @@ -1413,7 +1413,7 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call | Exp.Lvar _ | Exp.Var _ -> Pvar.mk_abduced_ref_param callee_pname actual_index callee_loc | _ - -> failwithf "Unexpected variable expression %a" Exp.pp actual + -> L.(die InternalError) "Unexpected variable expression %a" Exp.pp actual in let already_has_abduced_retval p = List.exists @@ -1439,7 +1439,8 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call let prop', fresh_fp_var = add_to_footprint tenv abduced typ prop in (prop', Sil.Eexp (fresh_fp_var, Sil.Inone)) | _ - -> failwith ("No need for abduction on non-pointer type " ^ Typ.to_string actual_typ) + -> L.(die InternalError) + "No need for abduction on non-pointer type %s" (Typ.to_string actual_typ) in let filtered_sigma = List.map diff --git a/infer/src/base/CommandLineOption.ml b/infer/src/base/CommandLineOption.ml index ee969bcfc..722315dd5 100644 --- a/infer/src/base/CommandLineOption.ml +++ b/infer/src/base/CommandLineOption.ml @@ -12,6 +12,7 @@ open! IStd module F = Format module YBU = Yojson.Basic.Util +module L = SimpleLogging let ( = ) = String.equal @@ -34,7 +35,7 @@ let strict_mode_env_var = "INFER_STRICT_MODE" let strict_mode = is_env_var_set strict_mode_env_var let warnf = - if strict_mode then failwithf + if strict_mode then fun fmt -> L.(die UserError) fmt else if not is_originator then fun fmt -> F.ifprintf F.err_formatter fmt else F.eprintf @@ -183,7 +184,7 @@ let check_no_duplicates desc_list = | [] | [_] -> true | (x, _, _) :: (y, _, _) :: _ when x <> "" && x = y - -> failwith ("Multiple definitions of command line option: " ^ x) + -> L.(die InternalError) "Multiple definitions of command line option: %s" x | _ :: tl -> check_for_duplicates_ tl in @@ -286,7 +287,7 @@ let deprecate_desc parse_mode ~long ~short ~deprecated desc = -> spec in let deprecated_decode_json ~inferconfig_dir j = - warnf "WARNING: in .inferconfig: '%s' is deprecated. Use '%s' instead.@." deprecated long ; + warnf "WARNING: in .inferconfig: '%s' is deprecated. Use '%s' instead." deprecated long ; desc.decode_json ~inferconfig_dir j in { long= "" @@ -510,9 +511,10 @@ let mk_path_helper ~setter ~default_to_string ~default ~deprecated ~long ~short let abs_path = normalize_path_in_args_being_parsed ~is_anon_arg:false str in setter var abs_path) ~mk_spec:(fun set -> String set ) -let mk_path ~default ?(deprecated= []) ~long ?short ?parse_mode ?in_help ?(meta= "path") = +let mk_path ~default ?(f= Fn.id) ?(deprecated= []) ~long ?short ?parse_mode ?in_help + ?(meta= "path") = mk_path_helper - ~setter:(fun var x -> var := x) + ~setter:(fun var x -> var := f x) ~decode_json:(path_json_decoder ~long) ~default_to_string:(fun s -> s) ~default ~deprecated ~long ~short ~parse_mode ~in_help ~meta @@ -975,7 +977,7 @@ let show_manual ?internal_section format default_doc command_opt = | Some command_doc, _, _ -> command_doc | None, _, _ - -> invalid_argf "No manual for internal command %s" (string_of_command command) + -> L.(die InternalError) "No manual for internal command %s" (string_of_command command) in let pp_meta f meta = match meta with "" -> () | meta -> F.fprintf f " $(i,%s)" (Cmdliner.Manpage.escape meta) diff --git a/infer/src/base/CommandLineOption.mli b/infer/src/base/CommandLineOption.mli index b6bb0911e..9f3df02eb 100644 --- a/infer/src/base/CommandLineOption.mli +++ b/infer/src/base/CommandLineOption.mli @@ -122,7 +122,7 @@ val mk_string_list : ?default:string list -> ?f:(string -> string) -> string lis An option "--[long]-reset" is automatically created that resets the list to [] when found on the command line. *) -val mk_path : default:string -> string ref t +val mk_path : default:string -> ?f:(string -> string) -> string ref t (** like [mk_string] but will resolve the string into an absolute path so that children processes agree on the absolute path that the option represents *) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index dfe148b3a..fa26058a4 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -14,8 +14,9 @@ open! PVariant (** Configuration values: either constant, determined at compile time, or set at startup time by system calls, environment variables, or command line options *) -module CLOpt = CommandLineOption module F = Format +module CLOpt = CommandLineOption +module L = SimpleLogging type analyzer = | BiAbduction @@ -1555,19 +1556,15 @@ and specs_library = (* Given a filename with a list of paths, convert it into a list of string iff they are absolute *) let read_specs_dir_list_file fname = - let validate_path path = - if Filename.is_relative path then - failwith ("Failing because path " ^ path ^ " is not absolute") - in match Utils.read_file (resolve fname) with | Ok pathlist - -> List.iter ~f:validate_path pathlist ; pathlist + -> pathlist | Error error - -> failwithf "cannot read file '%s' from cwd '%s': %s" fname (Sys.getcwd ()) error + -> L.(die UserError) "cannot read file '%s' from cwd '%s': %s" fname (Sys.getcwd ()) error in (* Add the newline-separated directories listed in to the list of directories to be searched for .spec files *) - CLOpt.mk_string ~deprecated:["specs-dir-list-file"; "-specs-dir-list-file"] + CLOpt.mk_path ~deprecated:["specs-dir-list-file"; "-specs-dir-list-file"] ~long:"specs-library-index" ~default:"" ~f:(fun file -> specs_library := read_specs_dir_list_file file @ !specs_library ; @@ -1787,12 +1784,34 @@ let post_parsing_initialization command_opt = | `None -> () ) ; if !version <> `None || !help <> `None then exit 0 ; - (* Core sets a verbose exception handler by default, with backtrace. This is good for developers - but in user-mode we want something lighter weight. *) - if not !developer_mode then - Caml.Printexc.set_uncaught_exception_handler (fun exn _ -> - let exn_msg = match exn with Failure msg -> msg | _ -> Caml.Printexc.to_string exn in - Format.eprintf "ERROR: %s@." exn_msg ) ; + let uncaught_exception_handler exn raw_backtrace = + let backtrace, should_print_backtrace_default = + match exn with + | L.InferExternalError (_, bt) | L.InferInternalError (_, bt) + -> (bt, true) + | L.InferUserError (_, bt) + -> (bt, false) + | _ + -> (Caml.Printexc.raw_backtrace_to_string raw_backtrace, true) + in + let print_exception () = + match exn with + | Failure msg + -> F.eprintf "ERROR: %s@\n" msg + | L.InferExternalError (msg, _) + -> F.eprintf "External Error: %s@\n" msg + | L.InferInternalError (msg, _) + -> F.eprintf "Internal Error: %s@\n" msg + | L.InferUserError (msg, _) + -> F.eprintf "Usage Error: %s@\n" msg + | _ + -> F.eprintf "Uncaught error: %s@\n" (Exn.to_string exn) + in + if should_print_backtrace_default || !developer_mode then prerr_endline backtrace ; + print_exception () ; + exit (L.exit_code_of_exception exn) + in + Caml.Printexc.set_uncaught_exception_handler uncaught_exception_handler ; F.set_margin !margin ; let set_minor_heap_size nMb = (* increase the minor heap size to speed up gc *) @@ -1853,7 +1872,7 @@ let process_iphoneos_target_sdk_version_path_regex args = | Some (path, version) -> {path= Str.regexp path; version} | None - -> failwithf + -> L.(die UserError) "Incorrect format for the option iphoneos-target-sdk_version-path-regex. The correct format is path:version but got %s" arg in diff --git a/infer/src/base/DB.ml b/infer/src/base/DB.ml index eb6c4e9a4..2bdf5a3e0 100644 --- a/infer/src/base/DB.ml +++ b/infer/src/base/DB.ml @@ -130,7 +130,7 @@ let file_modified_time ?(symlink= false) fname = try let stat = (if symlink then Unix.lstat else Unix.stat) fname in stat.Unix.st_mtime - with Unix.Unix_error _ -> failwithf "File %s does not exist." fname + with Unix.Unix_error _ -> L.(die InternalError) "File %s does not exist." fname let filename_create_dir fname = let dirname = Filename.dirname fname in @@ -172,7 +172,7 @@ let read_file_with_lock dir fname = Unix.lockf fd ~mode:Unix.F_RLOCK ~len:0L ; let buf = read_whole_file fd in Unix.lockf fd ~mode:Unix.F_ULOCK ~len:0L ; Unix.close fd ; Some buf - with Unix.Unix_error _ -> failwith "read_file_with_lock: Unix error" + with Unix.Unix_error _ -> L.(die ExternalError) "read_file_with_lock: Unix error" with Unix.Unix_error _ -> None (** {2 Results Directory} *) @@ -217,7 +217,7 @@ module Results_dir = struct (** initialize the results directory *) let init source = - if SourceFile.is_invalid source then invalid_arg "Invalid source file passed" ; + if SourceFile.is_invalid source then L.(die InternalError) "Invalid source file passed" ; Utils.create_dir Config.results_dir ; Utils.create_dir specs_dir ; Utils.create_dir (path_to_filename Abs_root [Config.attributes_dir_name]) ; @@ -245,7 +245,7 @@ module Results_dir = struct | filename :: dir_path -> (filename, dir_path) | [] - -> raise (Failure "create_path") + -> L.(die InternalError) "create_path" in let full_fname = Filename.concat (create dir_path) filename in Unix.openfile full_fname ~mode:Unix.([O_WRONLY; O_CREAT; O_TRUNC]) ~perm:0o777 diff --git a/infer/src/base/Logging.ml b/infer/src/base/Logging.ml index 5bd6c2b28..bdced9fc6 100644 --- a/infer/src/base/Logging.ml +++ b/infer/src/base/Logging.ml @@ -14,6 +14,7 @@ open! IStd module F = Format module CLOpt = CommandLineOption +include SimpleLogging (* log files *) (* make a copy of [f] *) @@ -35,15 +36,9 @@ let dup_formatter fmt1 fmt2 = ; out_spaces= (fun n -> out_funs1.out_spaces n ; out_funs2.out_spaces n) } ; f -(* should be set up to emit to a file later on; initially a string buffer so that logging is not - lost in the meantime *) -let log_file = - let b = Buffer.create 256 in - let fmt = - let f = F.formatter_of_buffer b in - if Config.print_logs then dup_formatter f F.err_formatter else f - in - ref (fmt, `Buffer b) +(* can be set up to emit to a file later on, but can also be left as-is and logging will only happen + on the console *) +let log_file = ref (F.err_formatter, `Console) type formatters = { file: F.formatter (** send to log file *) @@ -134,26 +129,20 @@ let close_logs () = List.iter ~f:close_fmt !logging_formatters ; let fmt, chan = !log_file in F.pp_print_flush fmt () ; - match chan with - | `Buffer b - -> prerr_endline (Buffer.contents b) - | `Channel c - -> Out_channel.close c + match chan with `Console -> () | `Channel c -> Out_channel.close c let () = Epilogues.register ~f:close_logs "flushing logs and closing log file" -let log_k ~to_console ?(to_file= true) ~k (lazy formatters) = +let log ~to_console ?(to_file= true) (lazy formatters) = match (to_console, to_file) with | false, false - -> F.ikfprintf k F.std_formatter + -> F.ifprintf F.std_formatter | true, _ when not Config.print_logs - -> F.kfprintf k !formatters.console_file + -> F.fprintf !formatters.console_file | _ -> (* to_console might be true, but in that case so is Config.print_logs so do not print to stderr because it will get logs from the log file already *) - F.kfprintf k !formatters.file - -let log = log_k ~k:ignore + F.fprintf !formatters.file let debug_file_fmts = register_formatter "debug" @@ -165,6 +154,8 @@ let external_error_file_fmts = register_formatter "extern err" let internal_error_file_fmts = register_formatter "intern err" +let phase_file_fmts = register_formatter "phase" + let progress_file_fmts = register_formatter "progress" let result_file_fmts = register_formatter ~use_stdout:true "result" @@ -173,6 +164,8 @@ let user_warning_file_fmts = register_formatter "user warn" let user_error_file_fmts = register_formatter "user err" +let phase fmt = log ~to_console:false phase_file_fmts fmt + let progress fmt = log ~to_console:(not Config.quiet) progress_file_fmts fmt let progress_bar text = @@ -198,9 +191,7 @@ let progressbar_timeout_event failure_kind = let user_warning fmt = log ~to_console:(not Config.quiet) user_warning_file_fmts fmt -let user_error_k ~k fmt = log_k ~to_console:(not Config.quiet) ~k user_error_file_fmts fmt - -let user_error fmt = user_error_k ~k:ignore fmt +let user_error fmt = log ~to_console:true user_error_file_fmts fmt type debug_level = Quiet | Medium | Verbose [@@deriving compare] @@ -242,14 +233,9 @@ let environment_info fmt = log ~to_console:false environment_info_file_fmts fmt let external_warning fmt = log ~to_console:(not Config.quiet) external_warning_file_fmts fmt -let external_error_k ~k fmt = log_k ~to_console:(not Config.quiet) ~k external_error_file_fmts fmt +let external_error fmt = log ~to_console:(not Config.quiet) external_error_file_fmts fmt -let external_error fmt = external_error_k ~k:ignore fmt - -let internal_error_k ~k fmt = - log_k ~to_console:Config.developer_mode ~k internal_error_file_fmts fmt - -let internal_error fmt = internal_error_k ~k:ignore fmt +let internal_error fmt = log ~to_console:Config.developer_mode internal_error_file_fmts fmt (** Type of location in ml source: __POS__ *) type ml_loc = string * int * int * int @@ -264,20 +250,24 @@ let pp_ml_loc_opt fmt ml_loc_opt = if Config.developer_mode then match ml_loc_opt with None -> () | Some ml_loc -> F.fprintf fmt "(%a)" pp_ml_loc ml_loc -type error = UserError | ExternalError | InternalError - -(* exit code 2 is used by the OCaml runtime in cases of uncaught exceptions, avoid it *) -let exit_code_of_kind = function UserError -> 1 | ExternalError -> 10 | InternalError -> 3 - -let log_of_kind = function +let log_of_kind error fmt = + match error with | UserError - -> user_error_k + -> log ~to_console:false user_error_file_fmts fmt | ExternalError - -> external_error_k + -> log ~to_console:false external_error_file_fmts fmt | InternalError - -> internal_error_k - -let die error msg = log_of_kind error ~k:(fun _ -> exit (exit_code_of_kind error)) msg + -> log ~to_console:false internal_error_file_fmts fmt + +let die error msg = + F.kasprintf + (fun s -> + (* backtraces contain line breaks, which results in lines without the [pid][error kind] prefix in the logs if printed as-is *) + Exn.backtrace () |> String.split ~on:'\n' + |> List.iter ~f:(fun line -> log_of_kind error "%s@\n" line) ; + log_of_kind error "%s@\n" s ; + die error "%s" s) + msg (* create new channel from the log file, and dumps the contents of the temporary log buffer there *) let setup_log_file () = @@ -285,7 +275,7 @@ let setup_log_file () = | _, `Channel _ -> (* already set up *) () - | _, `Buffer b + | _, `Console -> let fmt, chan, preexisting_logfile = (* assumes Config.results_dir exists already *) let logfile_path = Config.results_dir ^/ Config.log_file in @@ -300,7 +290,9 @@ let setup_log_file () = log_file := (fmt, `Channel chan) ; if preexisting_logfile then is_newline := false ; reset_formatters () ; - Buffer.output_buffer chan b + if CLOpt.is_originator && preexisting_logfile then + phase + "============================================================@\n= New infer execution begins@\n============================================================" (** type of printable elements *) type print_type = @@ -349,7 +341,7 @@ type print_action = print_type * Obj.t (** data to be printed *) let delayed_actions = ref [] (** hook for the current printer of delayed print actions *) -let printer_hook = ref (fun _ -> failwith "uninitialized printer hook") +let printer_hook = ref (fun _ -> SimpleLogging.(die InternalError) "uninitialized printer hook") (** extend the current print log *) let add_print_action pact = diff --git a/infer/src/base/Logging.mli b/infer/src/base/Logging.mli index 53b9f1c0e..53a5e3705 100644 --- a/infer/src/base/Logging.mli +++ b/infer/src/base/Logging.mli @@ -14,6 +14,11 @@ open! IStd module F = Format +(* If Logging has not been set up yet, SimpleLogging can be used instead. Prefer to use the + functions here, as they can do more logging. *) + +include module type of SimpleLogging + val environment_info : ('a, F.formatter, unit) format -> 'a (** log information about the environment *) @@ -58,15 +63,6 @@ type debug_level = val debug : debug_kind -> debug_level -> ('a, F.formatter, unit) format -> 'a (** log debug info *) -(** kind of error for [die], with similar semantics as above *) -type error = UserError | ExternalError | InternalError - -val die : error -> ('a, F.formatter, unit, _) format4 -> 'a -(** Print message and exit. The error code depends on [error]. - - Do not use lightly: failing hard should not be considered unless it's impossible to keep - going. *) - (** Type of location in ml source: __POS__ *) type ml_loc = string * int * int * int diff --git a/infer/src/base/SimpleLogging.ml b/infer/src/base/SimpleLogging.ml new file mode 100644 index 000000000..c1d3f902e --- /dev/null +++ b/infer/src/base/SimpleLogging.ml @@ -0,0 +1,41 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) +open! IStd +module F = Format + +type error = ExternalError | InternalError | UserError + +exception InferExternalError of string * string + +exception InferInternalError of string * string + +exception InferUserError of string * string + +let raise_error error msg backtrace = + match error with + | ExternalError + -> raise (InferExternalError (msg, backtrace)) + | InternalError + -> raise (InferInternalError (msg, backtrace)) + | UserError + -> raise (InferUserError (msg, backtrace)) + +let die error msg = + let backtrace = Exn.backtrace () in + F.kasprintf (fun s -> raise_error error s backtrace) msg + +let exit_code_of_exception = function + | InferUserError _ + -> 1 + | InferExternalError _ + -> 3 + | InferInternalError _ + -> 4 + | _ + -> (* exit code 2 is used by the OCaml runtime in cases of uncaught exceptions *) 2 diff --git a/infer/src/base/SimpleLogging.mli b/infer/src/base/SimpleLogging.mli new file mode 100644 index 000000000..c6bcd7e3d --- /dev/null +++ b/infer/src/base/SimpleLogging.mli @@ -0,0 +1,26 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! IStd + +(* WARNING: ONLY USE IF Logging IS NOT AVAILABLE TO YOU FOR SOME REASON (e.g., inside Config). *) + +exception InferExternalError of string * string + +exception InferInternalError of string * string + +exception InferUserError of string * string + +(** kind of error for [die], with similar semantics as [Logging.{external,internal,user}_error] *) +type error = ExternalError | InternalError | UserError + +val exit_code_of_exception : Exn.t -> int + +val die : error -> ('a, Format.formatter, unit, _) format4 -> 'a +(** Raise the corresponding exception. *) diff --git a/infer/src/base/SourceFile.ml b/infer/src/base/SourceFile.ml index f64157845..d63251c4d 100644 --- a/infer/src/base/SourceFile.ml +++ b/infer/src/base/SourceFile.ml @@ -39,7 +39,7 @@ module Set = Caml.Set.Make (OrderedSourceFile) let from_abs_path ?(warn_on_error= true) fname = if Filename.is_relative fname then - failwithf "ERROR: Path %s is relative, when absolute path was expected .@." fname ; + L.(die InternalError) "Path '%s' is relative, when absolute path was expected." fname ; (* try to get realpath of source file. Use original if it fails *) let fname_real = try Utils.realpath ~warn_on_error fname @@ -74,7 +74,7 @@ let pp fmt fname = Format.fprintf fmt "%s" (to_string fname) let to_abs_path fname = match fname with | Invalid origin - -> invalid_arg ("cannot be called with Invalid source file originating in " ^ origin) + -> L.(die InternalError) "cannot be called with Invalid source file originating in %s" origin | RelativeProjectRoot path -> Filename.concat Config.project_root path | RelativeInferModel path @@ -96,7 +96,7 @@ let is_invalid = function Invalid _ -> true | _ -> false let is_infer_model source_file = match source_file with | Invalid origin - -> invalid_arg ("cannot be called with Invalid source file from " ^ origin) + -> L.(die InternalError) "cannot be called with Invalid source file from %s" origin | RelativeProjectRoot _ | Absolute _ -> false | RelativeInferModel _ @@ -112,7 +112,7 @@ let is_cpp_model file = let is_under_project_root = function | Invalid origin - -> invalid_arg ("cannot be called with Invalid source file from " ^ origin) + -> L.(die InternalError) "cannot be called with Invalid source file from %s" origin | RelativeProjectRoot _ -> true | Absolute _ | RelativeInferModel _ diff --git a/infer/src/base/Utils.ml b/infer/src/base/Utils.ml index 5d7a62cb9..20db44206 100644 --- a/infer/src/base/Utils.ml +++ b/infer/src/base/Utils.ml @@ -11,6 +11,7 @@ open! IStd open! PVariant module F = Format module Hashtbl = Caml.Hashtbl +module L = SimpleLogging (** initial process times *) let initial_times = Unix.times () @@ -211,7 +212,7 @@ let shell_escape_command cmd = let create_dir dir = try if (Unix.stat dir).Unix.st_kind <> Unix.S_DIR then - failwithf "file %s exists and is not a directory@." dir + L.(die ExternalError) "file '%s' already exists and is not a directory" dir with Unix.Unix_error _ -> try Unix.mkdir dir ~perm:0o700 with Unix.Unix_error _ -> @@ -220,7 +221,7 @@ let create_dir dir = try Polymorphic_compare.( = ) (Unix.stat dir).Unix.st_kind Unix.S_DIR with Unix.Unix_error _ -> false in - if not created_concurrently then failwithf "cannot create directory %s@." dir + if not created_concurrently then L.(die ExternalError) "cannot create directory '%s'" dir let realpath_cache = Hashtbl.create 1023 diff --git a/infer/src/bufferoverrun/bufferOverrunChecker.ml b/infer/src/bufferoverrun/bufferOverrunChecker.ml index fa4012d98..c99d2a9f7 100644 --- a/infer/src/bufferoverrun/bufferOverrunChecker.ml +++ b/infer/src/bufferoverrun/bufferOverrunChecker.ml @@ -109,9 +109,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct mem |> Dom.Mem.add_stack (Loc.of_pvar array_pvar) v |> set_uninitialized node typ (Dom.Val.get_array_locs v) | [_; _] - -> failwithf "Unexpected type of arguments for __set_array_length()" + -> L.(die InternalError) "Unexpected type of arguments for __set_array_length()" | _ - -> failwithf "Unexpected number of arguments for __set_array_length()" + -> L.(die InternalError) "Unexpected number of arguments for __set_array_length()" let handle_unknown_call : Typ.Procname.t -> (Ident.t * Typ.t) option -> Typ.Procname.t -> (Exp.t * Typ.t) list diff --git a/infer/src/bufferoverrun/itv.ml b/infer/src/bufferoverrun/itv.ml index bf8e5eccc..191825c37 100644 --- a/infer/src/bufferoverrun/itv.ml +++ b/infer/src/bufferoverrun/itv.ml @@ -370,14 +370,14 @@ module Bound = struct let widen_l : t -> t -> t = fun x y -> assert (x <> Bot && y <> Bot) ; - if equal x PInf || equal y PInf then failwith "Lower bound cannot be +oo." + if equal x PInf || equal y PInf then L.(die InternalError) "Lower bound cannot be +oo." else if le x y then x else MInf let widen_u : t -> t -> t = fun x y -> assert (x <> Bot && y <> Bot) ; - if equal x MInf || equal y MInf then failwith "Upper bound cannot be -oo." + if equal x MInf || equal y MInf then L.(die InternalError) "Upper bound cannot be -oo." else if le y x then x else PInf @@ -852,14 +852,14 @@ let top : t = NonBottom ItvPure.top let lb : t -> Bound.t = function | NonBottom x -> ItvPure.lb x - | _ - -> raise (Failure "lower bound of bottom") + | Bottom + -> L.(die InternalError) "lower bound of bottom" let ub : t -> Bound.t = function | NonBottom x -> ItvPure.ub x - | _ - -> raise (Failure "upper bound of bottom") + | Bottom + -> L.(die InternalError) "upper bound of bottom" let of_int : int -> astate = fun n -> NonBottom (ItvPure.of_int n) diff --git a/infer/src/checkers/BoundedCallTree.ml b/infer/src/checkers/BoundedCallTree.ml index 09f93a138..f23a4b3e8 100644 --- a/infer/src/checkers/BoundedCallTree.ml +++ b/infer/src/checkers/BoundedCallTree.ml @@ -112,7 +112,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Typ.Procname.C _ -> true (* Needed for test code. *) | Typ.Procname.Block _ | Typ.Procname.Linters_dummy_method - -> failwith "Proc type not supported by crashcontext: block" + -> L.(die InternalError) "Proc type not supported by crashcontext: block" in String.equal frame.Stacktrace.method_str (Typ.Procname.get_method caller) && matches_class caller @@ -166,7 +166,7 @@ let loaded_stacktraces = let checker {Callbacks.proc_desc; tenv; get_proc_desc; summary} : Specs.summary = ( match loaded_stacktraces with | None - -> failwith + -> L.(die UserError) "Missing command line option. Either '--stacktrace stack.json' or '--stacktrace-dir ./dir' must be used when running '-a crashcontext'. This options expects a JSON formated stack trace or a directory containing multiple such traces, respectively. See tests/codetoanalyze/java/crashcontext/*.json for examples of the expected format." | Some stacktraces -> let extras = {get_proc_desc; stacktraces} in diff --git a/infer/src/checkers/IdAccessPathMapDomain.ml b/infer/src/checkers/IdAccessPathMapDomain.ml index 124708f5d..75aeb9c67 100644 --- a/infer/src/checkers/IdAccessPathMapDomain.ml +++ b/infer/src/checkers/IdAccessPathMapDomain.ml @@ -9,6 +9,7 @@ open! IStd module IdMap = Var.Map +module L = Logging type astate = AccessPath.t IdMap.t @@ -22,7 +23,8 @@ let check_invariant ap1 ap2 = function () | id -> if not (AccessPath.equal ap1 ap2) then - failwithf "Id %a maps to both %a and %a@." Var.pp id AccessPath.pp ap1 AccessPath.pp ap2 + L.(die InternalError) + "Id %a maps to both %a and %a" Var.pp id AccessPath.pp ap1 AccessPath.pp ap2 let ( <= ) ~lhs ~rhs = if phys_equal lhs rhs then true diff --git a/infer/src/checkers/NullabilitySuggest.ml b/infer/src/checkers/NullabilitySuggest.ml index 64f7f1e12..a0d96d5af 100644 --- a/infer/src/checkers/NullabilitySuggest.ml +++ b/infer/src/checkers/NullabilitySuggest.ml @@ -181,4 +181,4 @@ let checker {Callbacks.summary; proc_desc; tenv} = | Some (post, _) -> report post proc_data ; summary | None - -> failwithf "Analyzer failed to compute post for %a" Typ.Procname.pp proc_name + -> L.(die InternalError) "Analyzer failed to compute post for %a" Typ.Procname.pp proc_name diff --git a/infer/src/checkers/SimpleChecker.ml b/infer/src/checkers/SimpleChecker.ml index 6e150c755..ea337e750 100644 --- a/infer/src/checkers/SimpleChecker.ml +++ b/infer/src/checkers/SimpleChecker.ml @@ -56,9 +56,8 @@ module Make (Spec : Spec) : S = struct let iters_befor_timeout = 1000 in (* failsafe for accidental non-finite height domains *) if num_iters >= iters_befor_timeout then - failwith - ( "Stopping analysis after 1000 iterations without convergence." - ^ "Make sure your domain is finite height." ) + L.(die InternalError) + "Stopping analysis after 1000 iterations without convergence. Make sure your domain is finite height." else widen ~prev ~next ~num_iters end diff --git a/infer/src/checkers/SinkTrace.ml b/infer/src/checkers/SinkTrace.ml index 43771283e..53e09dc48 100644 --- a/infer/src/checkers/SinkTrace.ml +++ b/infer/src/checkers/SinkTrace.ml @@ -80,7 +80,7 @@ module Make (TraceElem : TraceElem.S) = struct | [report] -> Some report | _ - -> failwithf "Should not get >1 report for 1 sink" + -> L.(die InternalError) "Should not get >1 report for 1 sink" let pp fmt t = let pp_passthroughs_if_not_empty fmt p = diff --git a/infer/src/checkers/Siof.ml b/infer/src/checkers/Siof.ml index 3c60d9293..f776f0e31 100644 --- a/infer/src/checkers/Siof.ml +++ b/infer/src/checkers/Siof.ml @@ -184,7 +184,7 @@ let is_foreign tu_opt (v, _) = | TUExtern, Some _ -> true | _, None - -> invalid_arg "cannot be called with translation unit set to None" + -> L.(die InternalError) "cannot be called with translation unit set to None" let report_siof summary trace pdesc gname loc = let tu_opt = diff --git a/infer/src/checkers/Stacktrace.ml b/infer/src/checkers/Stacktrace.ml index f12fa3475..d8d3c294e 100644 --- a/infer/src/checkers/Stacktrace.ml +++ b/infer/src/checkers/Stacktrace.ml @@ -11,6 +11,7 @@ open! IStd module F = Format +module L = Logging type frame = {class_str: string; method_str: string; file_str: string; line_num: int option} @@ -86,7 +87,7 @@ let of_string s = let parsed = List.map ~f:parse_stack_frame trace in make exception_name parsed | [] - -> failwith "Empty stack trace" + -> L.(die UserError) "Empty stack trace" let of_json filename json = let exception_name_key = "exception_type" in @@ -94,7 +95,7 @@ let of_json filename json = let extract_json_member key = match Yojson.Basic.Util.member key json with | `Null - -> failwithf "Missing key in supplied JSON data: %s (in file %s)" key filename + -> L.(die UserError) "Missing key in supplied JSON data: %s (in file %s)" key filename | item -> item in @@ -109,4 +110,5 @@ let of_json filename json = let of_json_file filename = try of_json filename (Yojson.Basic.from_file filename) with Sys_error msg | Yojson.Json_error msg -> - failwithf "Could not read or parse the supplied JSON stacktrace file %s :@\n %s" filename msg + L.(die UserError) + "Could not read or parse the supplied JSON stacktrace file %s :@\n %s" filename msg diff --git a/infer/src/checkers/ThreadSafety.ml b/infer/src/checkers/ThreadSafety.ml index e9123979f..df853de46 100644 --- a/infer/src/checkers/ThreadSafety.ml +++ b/infer/src/checkers/ThreadSafety.ml @@ -26,7 +26,7 @@ let make_excluder locks threads = if locks && not threads then ThreadSafetyDomain.Excluder.Lock else if not locks && threads then ThreadSafetyDomain.Excluder.Thread else if locks && threads then ThreadSafetyDomain.Excluder.Both - else failwithf "called when neither lock nor thread known" + else L.(die InternalError) "called when neither lock nor thread known" module TransferFunctions (CFG : ProcCfg.S) = struct module CFG = CFG @@ -513,8 +513,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Some HilExp.AccessPath receiver_ap -> receiver_ap | _ - -> failwithf "Call to %a is marked as a container write, but has no receiver" - Typ.Procname.pp callee_pname + -> L.(die InternalError) + "Call to %a is marked as a container write, but has no receiver" Typ.Procname.pp + callee_pname in match get_container_access callee_pname tenv with | Some ContainerWrite @@ -599,7 +600,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct in {astate with attribute_map} | None - -> failwithf "Procedure %a specified as returning boolean, but returns nothing" + -> L.(die InternalError) + "Procedure %a specified as returning boolean, but returns nothing" Typ.Procname.pp callee_pname ) | Unknown -> astate @@ -622,7 +624,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct in {astate with attribute_map} | None - -> failwithf "Procedure %a specified as returning boolean, but returns nothing" + -> L.(die InternalError) + "Procedure %a specified as returning boolean, but returns nothing" Typ.Procname.pp callee_pname ) | NoEffect -> match get_summary pdesc callee_pname actuals loc tenv with @@ -872,7 +875,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Call (_, Indirect _, _, _, _) -> match Procdesc.get_proc_name pdesc with | Typ.Procname.Java _ - -> failwithf "Unexpected indirect call instruction %a" HilInstr.pp instr + -> L.(die InternalError) "Unexpected indirect call instruction %a" HilInstr.pp instr | _ -> astate end @@ -1545,7 +1548,7 @@ let quotient_access_map acc_map = -> ThreadSafetyDomain.Access.equal k k') m in - if AccessListMap.is_empty k_part then failwith "may_alias is not reflexive!" ; + if AccessListMap.is_empty k_part then L.(die InternalError) "may_alias is not reflexive!" ; let k_accesses = AccessListMap.fold (fun _ v acc' -> List.append v acc') k_part [] in let new_acc = AccessListMap.add k k_accesses acc in aux new_acc non_k_part diff --git a/infer/src/checkers/ThreadSafetyConfig.ml b/infer/src/checkers/ThreadSafetyConfig.ml index 47e7fbc75..008e6b653 100644 --- a/infer/src/checkers/ThreadSafetyConfig.ml +++ b/infer/src/checkers/ThreadSafetyConfig.ml @@ -9,11 +9,13 @@ open! IStd module F = Format +module L = Logging module AnnotationAliases = struct let of_json = function | `List aliases -> List.map ~f:Yojson.Basic.Util.to_string aliases | _ - -> failwith "Couldn't parse thread-safety annotation aliases; expected list of strings" + -> L.(die UserError) + "Couldn't parse thread-safety annotation aliases; expected list of strings" end diff --git a/infer/src/checkers/annotationReachability.ml b/infer/src/checkers/annotationReachability.ml index 643560b5d..e4dda1518 100644 --- a/infer/src/checkers/annotationReachability.ml +++ b/infer/src/checkers/annotationReachability.ml @@ -247,7 +247,6 @@ module AnnotationSpec = struct (* The default sanitizer does not sanitize anything *) let default_sanitizer _ _ = false - end module StandardAnnotationSpec = struct @@ -427,7 +426,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Sil.Prune (exp, _, _, _) when prunes_tracking_var astate exp -> Domain.stop_tracking astate | Sil.Call (None, _, _, _, _) - -> failwith "Expecting a return identifier" + -> L.(die InternalError) "Expecting a return identifier" | _ -> astate end diff --git a/infer/src/checkers/printfArgs.ml b/infer/src/checkers/printfArgs.ml index b00eb26a9..f3c902282 100644 --- a/infer/src/checkers/printfArgs.ml +++ b/infer/src/checkers/printfArgs.ml @@ -149,7 +149,7 @@ let check_printf_args_ok tenv (node: Procdesc.Node.t) (instr: Sil.instr) | Exp.Const c -> PatternMatch.java_get_const_type_name c | _ - -> raise (Failure "Could not resolve fixed type name") + -> L.(die InternalError) "Could not resolve fixed type name" in match instr with | Sil.Call (_, Exp.Const Const.Cfun pn, args, cl, _) -> ( diff --git a/infer/src/clang/ClangPointers.ml b/infer/src/clang/ClangPointers.ml index b3a0c2f10..a9a6ca1e2 100644 --- a/infer/src/clang/ClangPointers.ml +++ b/infer/src/clang/ClangPointers.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging type t = Clang_ast_t.pointer @@ -34,8 +35,8 @@ let visit_ast ?(visit_decl= empty_v) ?(visit_stmt= empty_v) ?(visit_type= empty_ | None -> () | Some error - -> failwithf "visiting the clang AST failed with error %s" - (Ag_util.Validation.string_of_error error) + -> L.(die InternalError) + "visiting the clang AST failed with error %s" (Ag_util.Validation.string_of_error error) let get_ptr_from_node node = match node with diff --git a/infer/src/clang/ClangWrapper.ml b/infer/src/clang/ClangWrapper.ml index 660c6941e..b3ffb4b80 100644 --- a/infer/src/clang/ClangWrapper.ml +++ b/infer/src/clang/ClangWrapper.ml @@ -85,7 +85,7 @@ let normalize ~prog ~args : action_item list = | prog :: args -> ClangCommand.mk ClangQuotes.EscapedDoubleQuotes ~prog ~args | [] - -> failwith "ClangWrapper: argv cannot be empty" ) + -> L.(die InternalError) "ClangWrapper: argv cannot be empty" ) else if Str.string_match (Str.regexp "clang[^ :]*: warning: ") line 0 then ClangWarning line else ClangError line in @@ -112,7 +112,7 @@ let exec_action_item = function | ClangError error -> (* An error in the output of `clang -### ...`. Outputs the error and fail. This is because `clang -###` pretty much never fails, but warns of failures on stderr instead. *) - failwithf + L.(die UserError) "Failed to execute compilation command. Output:@\n%s@\n*** Infer needs a working compilation command to run." error | ClangWarning warning diff --git a/infer/src/clang/cAst_utils.ml b/infer/src/clang/cAst_utils.ml index f055fa25f..bfd73ad36 100644 --- a/infer/src/clang/cAst_utils.ml +++ b/infer/src/clang/cAst_utils.ml @@ -338,7 +338,7 @@ let rec is_objc_if_descendant ?(blacklist= default_blacklist) if_decl ancestors (* List of ancestors to check for and list of classes to short-circuit to false can't intersect *) if not String.Set.(is_empty (inter (of_list blacklist) (of_list ancestors))) then - failwith "Blacklist and ancestors must be mutually exclusive." + L.(die InternalError) "Blacklist and ancestors must be mutually exclusive." else match if_decl with | Some Clang_ast_t.ObjCInterfaceDecl (_, ndi, _, _, _) diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index eeeb0e7ef..f55dc9d98 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging (* Helper functions *) let location_from_stmt lctx stmt = @@ -45,9 +46,9 @@ let decl_ref_or_selector_name an = | [(Ctl_parser_types.Decl _ as decl_an)] -> "The reference " ^ Ctl_parser_types.ast_node_name decl_an | _ - -> failwith - ( "decl_ref_or_selector_name must be called with a DeclRefExpr or an ObjCMessageExpr, but got " - ^ tag_name_of_node an ) + -> L.(die ExternalError) + "decl_ref_or_selector_name must be called with a DeclRefExpr or an ObjCMessageExpr, but got %s" + (tag_name_of_node an) let iphoneos_target_sdk_version _ = match Config.iphoneos_target_sdk_version with Some f -> f | None -> "0" @@ -62,9 +63,9 @@ let available_ios_sdk an = | None -> "" ) | _ - -> failwith - ( "available_ios_sdk must be called with a DeclRefExpr or an ObjCMessageExpr, but got " - ^ tag_name_of_node an ) + -> L.(die ExternalError) + "available_ios_sdk must be called with a DeclRefExpr or an ObjCMessageExpr, but got %s" + (tag_name_of_node an) let class_available_ios_sdk an = match CPredicates.receiver_method_call an with @@ -75,17 +76,18 @@ let class_available_ios_sdk an = | None -> "" ) | None - -> failwith - ( "class_available_ios_sdk must be called with ObjCMessageExpr, but got " - ^ tag_name_of_node an ) + -> L.(die ExternalError) + "class_available_ios_sdk must be called with ObjCMessageExpr, but got %s" + (tag_name_of_node an) let receiver_method_call an = match CPredicates.receiver_method_call an with | Some decl -> Ctl_parser_types.ast_node_name (Ctl_parser_types.Decl decl) | _ - -> failwith - ("receiver_method_call must be called with ObjCMessageExpr, but got " ^ tag_name_of_node an) + -> L.(die ExternalError) + "receiver_method_call must be called with ObjCMessageExpr, but got %s" + (tag_name_of_node an) let ivar_name an = let open Clang_ast_t in diff --git a/infer/src/clang/cFrontend_checkers_main.ml b/infer/src/clang/cFrontend_checkers_main.ml index 42207b342..e45eadf03 100644 --- a/infer/src/clang/cFrontend_checkers_main.ml +++ b/infer/src/clang/cFrontend_checkers_main.ml @@ -14,7 +14,7 @@ let already_imported_files = ref [] let rec parse_import_file import_file channel = if List.mem ~equal:String.equal !already_imported_files import_file then - failwith ("Cyclic imports: file '" ^ import_file ^ "' was already imported.") + L.(die ExternalError) "Cyclic imports: file '%s' was already imported." import_file else match CTLParserHelper.parse_al_file import_file channel with | Some diff --git a/infer/src/clang/cFrontend_errors.ml b/infer/src/clang/cFrontend_errors.ml index ecb3d02d4..b319ae603 100644 --- a/infer/src/clang/cFrontend_errors.ml +++ b/infer/src/clang/cFrontend_errors.ml @@ -24,7 +24,7 @@ let filter_parsed_linters_developer parsed_linters = if List.length parsed_linters > 1 && Config.linters_developer_mode then match Config.linter with | None - -> failwith + -> L.(die UserError) "In linters developer mode you should debug only one linter at a time. This is important for debugging the rule. Pass the flag --linter to specify the linter you want to debug." | Some lint -> List.filter ~f:(fun (rule: linter) -> String.equal rule.issue_desc.id lint) parsed_linters @@ -266,7 +266,7 @@ let rec apply_substitution f sub = let expand_formula phi _map _error_msg = let fail_with_circular_macro_definition name error_msg = - failwithf "Macro '%s' has a circular definition.@\n Cycle:@\n%s" name error_msg + L.(die ExternalError) "Macro '%s' has a circular definition.@\n Cycle:@\n%s" name error_msg in let open CTL in let rec expand acc map error_msg = @@ -289,9 +289,8 @@ let expand_formula phi _map _error_msg = let map' = ALVar.FormulaIdMap.add av (true, fparams, f1) map in expand f1_sub map' error_msg' | None - -> failwith - ( "Formula identifier '" ^ name - ^ "' is not called with the right number of parameters" ) + -> L.(die ExternalError) + "Formula identifier '%s' is not called with the right number of parameters" name with Not_found -> acc (* in this case it should be a predicate *) ) | Not f1 @@ -337,7 +336,7 @@ let rec expand_path paths path_map = try let paths = ALVar.VarMap.find path_var path_map in List.append paths (expand_path rest path_map) - with Not_found -> failwithf "Path variable %s not found. " path_var ) + with Not_found -> L.(die ExternalError) "Path variable %s not found. " path_var ) | path :: rest -> path :: expand_path rest path_map @@ -348,8 +347,8 @@ let _build_macros_map macros init_map = match data with | CTL.CLet (key, params, formula) -> if ALVar.FormulaIdMap.mem key map' then - failwith - ("Macro '" ^ ALVar.formula_id_to_string key ^ "' has more than one definition.") + L.(die ExternalError) + "Macro '%s' has more than one definition." (ALVar.formula_id_to_string key) else ALVar.FormulaIdMap.add key (false, params, formula) map' | _ -> map') @@ -369,7 +368,7 @@ let build_paths_map paths = match data with path_name, paths -> if ALVar.VarMap.mem path_name map' then - failwith ("Path '" ^ path_name ^ "' has more than one definition.") + L.(die ExternalError) "Path '%s' has more than one definition." path_name else ALVar.VarMap.add path_name paths map') ~init:init_map paths in diff --git a/infer/src/clang/cPredicates.ml b/infer/src/clang/cPredicates.ml index 20e32ef67..5a656d7b0 100644 --- a/infer/src/clang/cPredicates.ml +++ b/infer/src/clang/cPredicates.ml @@ -415,7 +415,7 @@ let captures_cxx_references an = List.length (captured_variables_cxx_ref an) > 0 let is_binop_with_kind an alexp_kind = let str_kind = ALVar.alexp_to_string alexp_kind in if not (Clang_ast_proj.is_valid_binop_kind_name str_kind) then - failwith ("Binary operator kind " ^ str_kind ^ " is not valid") ; + L.(die ExternalError) "Binary operator kind '%s' is not valid" str_kind ; match an with | Ctl_parser_types.Stmt Clang_ast_t.BinaryOperator (_, _, _, boi) -> ALVar.compare_str_with_alexp (Clang_ast_proj.string_of_binop_kind boi.boi_kind) alexp_kind @@ -425,7 +425,7 @@ let is_binop_with_kind an alexp_kind = let is_unop_with_kind an alexp_kind = let str_kind = ALVar.alexp_to_string alexp_kind in if not (Clang_ast_proj.is_valid_unop_kind_name str_kind) then - failwith ("Unary operator kind " ^ str_kind ^ " is not valid") ; + L.(die ExternalError) "Unary operator kind '%s' is not valid" str_kind ; match an with | Ctl_parser_types.Stmt Clang_ast_t.UnaryOperator (_, _, _, uoi) -> ALVar.compare_str_with_alexp (Clang_ast_proj.string_of_unop_kind uoi.uoi_kind) alexp_kind @@ -448,7 +448,7 @@ let has_cast_kind an alexp_kind = let is_node an nodename = let nodename_str = ALVar.alexp_to_string nodename in if not (Clang_ast_proj.is_valid_astnode_kind nodename_str) then - failwith ("Node " ^ nodename_str ^ " is not a valid AST node") ; + L.(die ExternalError) "Node '%s' is not a valid AST node" nodename_str ; let an_str = match an with | Ctl_parser_types.Stmt s diff --git a/infer/src/clang/cTL.ml b/infer/src/clang/cTL.ml index 6979eb9b5..95b124216 100644 --- a/infer/src/clang/cTL.ml +++ b/infer/src/clang/cTL.ml @@ -433,7 +433,7 @@ module Debug = struct | Eval_false -> "red" | _ - -> failwith "Tree is not fully evaluated" + -> L.(die InternalError) "Tree is not fully evaluated" in let label = let string_of_lcxt c = @@ -527,7 +527,7 @@ let create_ctl_evaluation_tracker source_file = | true, None -> ctl_evaluation_tracker := Some (Debug.EvaluationTracker.create source_file) | true, _ - -> failwith "A CTL evaluation tracker has already been created" + -> L.(die InternalError) "A CTL evaluation tracker has already been created" | _ -> () @@ -853,7 +853,7 @@ let rec eval_Atomic _pred_name args an lcxt = | "within_available_class_block", [], an -> CPredicates.within_available_class_block lcxt an | _ - -> failwith ("ERROR: Undefined Predicate or wrong set of arguments: '" ^ pred_name ^ "'") + -> L.(die ExternalError) "Undefined Predicate or wrong set of arguments: '%s'" pred_name (* an, lcxt |= EF phi <=> an, lcxt |= phi or exists an' in Successors(st): an', lcxt |= EF phi diff --git a/infer/src/clang/cType_to_sil_type.ml b/infer/src/clang/cType_to_sil_type.ml index e1a7ca8d7..87f0dd8bd 100644 --- a/infer/src/clang/cType_to_sil_type.ml +++ b/infer/src/clang/cType_to_sil_type.ml @@ -213,7 +213,7 @@ and type_ptr_to_type_desc translate_decl tenv type_ptr : Typ.desc = | Clang_ast_extend.ErrorType -> Typ.Tvoid | _ - -> raise (invalid_arg "unknown variant for type_ptr") + -> L.(die InternalError) "unknown variant for type_ptr" and qual_type_to_sil_type translate_decl tenv qual_type = let desc = type_ptr_to_type_desc translate_decl tenv qual_type.Clang_ast_t.qt_type_ptr in diff --git a/infer/src/clang/clang_ast_extend.ml b/infer/src/clang/clang_ast_extend.ml index aad5ecbe4..ee6a41772 100644 --- a/infer/src/clang/clang_ast_extend.ml +++ b/infer/src/clang/clang_ast_extend.ml @@ -8,10 +8,11 @@ *) open! IStd +(* This module adds more variants to some types in AST The implementation extends default one from + the facebook-clang-plugins repository *) + +module L = Logging -(* This module adds more variants to some types in AST *) -(* The implementation extends default one from *) -(* facebook-clang-plugins repository *) (* Type pointers *) type Clang_ast_types.TypePtr.t += | Builtin of Clang_ast_t.builtin_type_kind @@ -21,6 +22,24 @@ type Clang_ast_types.TypePtr.t += | DeclPtr of int | ErrorType +let rec type_ptr_to_string = function + | Clang_ast_types.TypePtr.Ptr raw + -> "clang_ptr_" ^ string_of_int raw + | Builtin t + -> "sil_" ^ Clang_ast_j.string_of_builtin_type_kind t + | PointerOf typ + -> "pointer_of_" ^ type_ptr_to_string typ.Clang_ast_t.qt_type_ptr + | ReferenceOf typ + -> "reference_of_" ^ type_ptr_to_string typ.Clang_ast_t.qt_type_ptr + | ClassType name + -> "class_name_" ^ Typ.Name.name name + | DeclPtr raw + -> "decl_ptr_" ^ string_of_int raw + | ErrorType + -> "error_type" + | _ + -> "unknown" + module TypePointerOrd = struct type t = Clang_ast_types.TypePtr.t @@ -66,8 +85,9 @@ module TypePointerOrd = struct -> -1 | ErrorType, ErrorType -> 0 - | _ - -> raise (invalid_arg "unexpected type_ptr variants: ") + | t1, t2 + -> L.(die InternalError) + "unexpected type_ptr variants: %s, %s" (type_ptr_to_string t1) (type_ptr_to_string t2) and compare_qual_type (qt1: Clang_ast_t.qual_type) (qt2: Clang_ast_t.qual_type) = if phys_equal qt1 qt2 then 0 @@ -90,21 +110,3 @@ module TypePointerOrd = struct end module TypePointerMap = Caml.Map.Make (TypePointerOrd) - -let rec type_ptr_to_string = function - | Clang_ast_types.TypePtr.Ptr raw - -> "clang_ptr_" ^ string_of_int raw - | Builtin t - -> "sil_" ^ Clang_ast_j.string_of_builtin_type_kind t - | PointerOf typ - -> "pointer_of_" ^ type_ptr_to_string typ.Clang_ast_t.qt_type_ptr - | ReferenceOf typ - -> "reference_of_" ^ type_ptr_to_string typ.Clang_ast_t.qt_type_ptr - | ClassType name - -> "class_name_" ^ Typ.Name.name name - | DeclPtr raw - -> "decl_ptr_" ^ string_of_int raw - | ErrorType - -> "error_type" - | _ - -> "unknown" diff --git a/infer/src/clang/ctl_parser.mly b/infer/src/clang/ctl_parser.mly index 00fb80326..23a8d2443 100644 --- a/infer/src/clang/ctl_parser.mly +++ b/infer/src/clang/ctl_parser.mly @@ -172,8 +172,8 @@ clause: { L.(debug Linters Verbose) "\tParsed SET clause@\n"; let alvar = match $2 with | "report_when" -> ALVar.Report_when - | _ -> failwith "string '%s' cannot be set to a variable. \ - Use the reserverd variable 'report_when'" in + | _ -> L.(die ExternalError) "string '%s' cannot be set to a variable. \ + Use the reserved variable 'report_when'" $2 in CTL.CSet (alvar, $4) } | SET WHITELIST_PATH ASSIGNMENT LEFT_BRACE path_list RIGHT_BRACE { CTL.CPath (`WhitelistPath, $5) } @@ -188,9 +188,10 @@ clause: | "mode" -> ALVar.Mode | "doc_url" -> ALVar.Doc_url | "name" -> ALVar.Name - | _ -> failwithf "string '%s' cannot be set in a SET clause. \ - Use either of: \ - 'doc_url', 'message', 'mode', 'name', 'severity' or 'suggestion'" $2 in + | _ -> L.(die ExternalError) + "string '%s' cannot be set in a SET clause. \ + Use either of: \ + 'doc_url', 'message', 'mode', 'name', 'severity' or 'suggestion'" $2 in CTL.CDesc (alvar, $4) } | let_clause { $1 } ; diff --git a/infer/src/harness/inhabit.ml b/infer/src/harness/inhabit.ml index 482330819..10b7726f3 100644 --- a/infer/src/harness/inhabit.ml +++ b/infer/src/harness/inhabit.ml @@ -213,8 +213,9 @@ let inhabit_call tenv (procname, receiver) cfg env = | formals, None -> formals | [], Some _ - -> failwithf "Expected at least one formal to bind receiver to in method %a" - Typ.Procname.pp procname + -> L.(die InternalError) + "Expected at least one formal to bind receiver to in method %a" Typ.Procname.pp + procname in let args, env = inhabit_args tenv formals cfg env in inhabit_call_with_args procname procdesc args env diff --git a/infer/src/integration/Buck.ml b/infer/src/integration/Buck.ml index 56b8cbfc1..85186e8a7 100644 --- a/infer/src/integration/Buck.ml +++ b/infer/src/integration/Buck.ml @@ -8,6 +8,7 @@ *) open! IStd +module L = Logging type target = {name: string; flavors: string list} @@ -19,7 +20,7 @@ let target_of_string target = | [name] -> {name; flavors= []} | _ - -> failwithf "cannot parse target %s" target + -> L.(die ExternalError) "cannot parse target %s" target let string_of_target {name; flavors} = let pp_string fmt s = Format.fprintf fmt "%s" s in @@ -49,7 +50,8 @@ let add_flavor_to_target target = | None, (BiAbduction | CaptureOnly | Checkers | Linters) -> add "infer-capture-all" | None, Crashcontext - -> failwithf "Analyzer %s is Java-only; not supported with Buck flavors" + -> L.(die UserError) + "Analyzer %s is Java-only; not supported with Buck flavors" (Config.string_of_analyzer Config.analyzer) let add_flavors_to_buck_command build_cmd = diff --git a/infer/src/integration/CaptureCompilationDatabase.ml b/infer/src/integration/CaptureCompilationDatabase.ml index fec347b2a..cbad46185 100644 --- a/infer/src/integration/CaptureCompilationDatabase.ml +++ b/infer/src/integration/CaptureCompilationDatabase.ml @@ -95,7 +95,8 @@ let get_compilation_database_files_buck ~prog ~args = in match exit_or_signal with | Error _ as status - -> failwithf "*** command failed:@\n*** %s@\n*** %s@." buck_targets_shell + -> L.(die ExternalError) + "*** command failed:@\n*** %s@\n*** %s@." buck_targets_shell (Unix.Exit_or_signal.to_string_hum status) | Ok () -> match output with @@ -111,8 +112,8 @@ let get_compilation_database_files_buck ~prog ~args = | [_; filename] -> `Raw filename :: compilation_database_files | _ - -> failwithf "Failed to parse `buck targets --show-output ...` line of output:@\n%s" - line + -> L.(die ExternalError) + "Failed to parse `buck targets --show-output ...` line of output:@\n%s" line in List.fold ~f:scan_output ~init:[] lines ) | _ diff --git a/infer/src/integration/Clang.ml b/infer/src/integration/Clang.ml index d20db40e2..4d371cae6 100644 --- a/infer/src/integration/Clang.ml +++ b/infer/src/integration/Clang.ml @@ -58,6 +58,7 @@ let capture compiler ~prog ~args = | Ok () -> () | Error _ as status - -> failwithf "*** capture command failed:@\n*** %s@\n*** %s@." + -> L.(die ExternalError) + "*** capture command failed:@\n*** %s@\n*** %s@." (String.concat ~sep:" " (prog :: args)) (Unix.Exit_or_signal.to_string_hum status) diff --git a/infer/src/integration/CompilationDatabase.ml b/infer/src/integration/CompilationDatabase.ml index cdd19e03b..b21cd2498 100644 --- a/infer/src/integration/CompilationDatabase.ml +++ b/infer/src/integration/CompilationDatabase.ml @@ -49,7 +49,7 @@ let decode_json_file (database: t) json_format = |> fst in L.(debug Capture Quiet) "parsing compilation database from %s@\n" json_path ; - let exit_format_error () = failwith "Json file doesn't have the expected format" in + let exit_format_error () = L.(die ExternalError) "Json file doesn't have the expected format" in let json = Yojson.Basic.from_file json_path in let get_dir el = match el with "directory", `String dir -> Some (to_string dir) | _ -> None in let get_file el = match el with "file", `String file -> Some (to_string file) | _ -> None in diff --git a/infer/src/integration/Driver.ml b/infer/src/integration/Driver.ml index 58c08abd1..9c8441b39 100644 --- a/infer/src/integration/Driver.ml +++ b/infer/src/integration/Driver.ml @@ -59,7 +59,7 @@ let build_system_exe_assoc = let build_system_of_exe_name name = try List.Assoc.find_exn ~equal:String.equal (List.Assoc.inverse build_system_exe_assoc) name - with Not_found -> invalid_argf "Unsupported build command %s" name + with Not_found -> L.(die InternalError) "Unsupported build command %s" name let string_of_build_system build_system = List.Assoc.find_exn ~equal:equal_build_system build_system_exe_assoc build_system @@ -474,7 +474,7 @@ let assert_supported_mode required_analyzer requested_mode_string = | `Xcode -> "clang and xcode" in - failwithf + L.(die UserError) "Unsupported build mode: %s@\nInfer was built with %s analyzers disabled.@ Please rebuild infer with %s enabled.@." requested_mode_string analyzer_string analyzer_string diff --git a/infer/src/integration/Javac.ml b/infer/src/integration/Javac.ml index 25d0d4c97..a1cdccd72 100644 --- a/infer/src/integration/Javac.ml +++ b/infer/src/integration/Javac.ml @@ -69,7 +69,7 @@ let compile compiler build_prog build_args = -> L.(debug Capture Quiet) "*** Failed: %a!@\n" Exn.pp exn ; k () | None, `UnixError (err, log) -> let verbose_errlog = Utils.with_file_in verbose_out_file ~f:In_channel.input_all in - failwithf + L.(die UserError) "@\n*** Failed to execute compilation command: %s@\n*** Command: %s@\n*** Output:@\n%s%s@\n*** Infer needs a working compilation command to run.@." (Unix.Exit_or_signal.to_string_hum (Error err)) shell_cmd log verbose_errlog | None, `ExceptionError exn diff --git a/infer/src/integration/Maven.ml b/infer/src/integration/Maven.ml index 284879ab2..8802ebf3f 100644 --- a/infer/src/integration/Maven.ml +++ b/infer/src/integration/Maven.ml @@ -83,7 +83,7 @@ let add_infer_profile_to_xml dir maven_xml infer_xml = -> (* closing the first tag, we're done *) () | [] - -> invalid_arg "ill-formed xml" ) + -> L.(die UserError) "ill-formed xml" ) | `Data data -> Xmlm.output xml_out elt_in ; ( match tag_stack with @@ -110,7 +110,7 @@ let add_infer_profile_to_xml dir maven_xml infer_xml = -> Xmlm.output infer_xml (`Dtd None) ) ; process_root maven_xml infer_xml ; Xmlm.eoi maven_xml |> ignore ; - if not (Xmlm.eoi maven_xml) then invalid_arg "More than one document" + if not (Xmlm.eoi maven_xml) then L.(die UserError) "More than one document" in process_document () @@ -171,6 +171,7 @@ let capture ~prog ~args = | Ok () -> () | Error _ as status - -> failwithf "*** Maven command failed:@\n*** %s@\n*** %s@\n" + -> L.(die UserError) + "*** Maven command failed:@\n*** %s@\n*** %s@\n" (String.concat ~sep:" " (prog :: capture_args)) (Unix.Exit_or_signal.to_string_hum status) diff --git a/infer/src/istd/IStd.ml b/infer/src/istd/IStd.ml index 68e9a228a..7ff37b4a1 100644 --- a/infer/src/istd/IStd.ml +++ b/infer/src/istd/IStd.ml @@ -63,8 +63,10 @@ module PVariant = struct let ( = ) (v1: [> ]) (v2: [> ]) = Polymorphic_compare.( = ) v1 v2 end -let failwithf fmt = - Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ())) Format.str_formatter fmt +let failwith _ : [`use_Logging_die_instead] = assert false -let invalid_argf fmt = - Format.kfprintf (fun _ -> invalid_arg (Format.flush_str_formatter ())) Format.str_formatter fmt +let failwithf _ : [`use_Logging_die_instead] = assert false + +let invalid_arg _ : [`use_Logging_die_instead] = assert false + +let invalid_argf _ : [`use_Logging_die_instead] = assert false diff --git a/infer/src/java/jClasspath.ml b/infer/src/java/jClasspath.ml index 083ac8061..c8421d0f0 100644 --- a/infer/src/java/jClasspath.ml +++ b/infer/src/java/jClasspath.ml @@ -37,14 +37,14 @@ let load_models_tenv zip_channel = Zip.copy_entry_to_file zip_channel entry temp_tenv_file ; match Tenv.load_from_file temp_tenv_filename with | None - -> failwith "Models tenv file could not be loaded" + -> L.(die InternalError) "Models tenv file could not be loaded" | Some tenv -> tenv with | Not_found - -> failwith "Models tenv not found in jar file" + -> L.(die InternalError) "Models tenv not found in jar file" | Sys_error msg - -> failwith ("Models jar could not be opened " ^ msg) + -> L.(die InternalError) "Models jar could not be opened: %s" msg in DB.file_remove temp_tenv_filename ; models_tenv @@ -65,7 +65,7 @@ let collect_specs_filenames jar_filename = let add_models jar_filename = models_jar := jar_filename ; if Sys.file_exists !models_jar = `Yes then collect_specs_filenames jar_filename - else failwith "Java model file not found" + else L.(die InternalError) "Java model file not found" let is_model procname = String.Set.mem !models_specs_filenames (Typ.Procname.to_filename procname) diff --git a/infer/src/java/jFrontend.ml b/infer/src/java/jFrontend.ml index 0248f9a29..9955578ea 100644 --- a/infer/src/java/jFrontend.ml +++ b/infer/src/java/jFrontend.ml @@ -99,7 +99,7 @@ let add_cmethod source_file program linereader icfg cm proc_name = | Some node -> node | None - -> failwithf "No exn node found for %s" (Typ.Procname.to_string proc_name) + -> L.(die InternalError) "No exn node found for %s" (Typ.Procname.to_string proc_name) in let instrs = JBir.code jbir_code in let context = JContext.create_context icfg procdesc jbir_code cn source_file program in diff --git a/infer/src/java/jMain.ml b/infer/src/java/jMain.ml index 1f2726e1c..91a17e176 100644 --- a/infer/src/java/jMain.ml +++ b/infer/src/java/jMain.ml @@ -71,7 +71,8 @@ let load_tenv () = | None -> Tenv.create () | Some _ when Config.models_mode - -> failwithf "Unexpected tenv file %s found while generating the models" + -> L.(die InternalError) + "Unexpected tenv file %s found while generating the models" (DB.filename_to_string DB.global_tenv_fname) | Some tenv -> tenv @@ -128,9 +129,9 @@ let main load_sources_and_classes = | true, false -> () | false, false - -> failwith "Java model file is required" + -> L.(die UserError) "Java model file is required" | true, true - -> failwith "Not expecting model file when analyzing the models" + -> L.(die UserError) "Not expecting model file when analyzing the models" | false, true -> JClasspath.add_models Config.models_jar ) ; JBasics.set_permissive true ; @@ -141,7 +142,7 @@ let main load_sources_and_classes = | `FromArguments path -> JClasspath.load_from_arguments path in - if String.Map.is_empty sources then failwith "Failed to load any Java source code" + if String.Map.is_empty sources then L.(die UserError) "Failed to load any Java source code" else do_all_files classpath sources classes let from_arguments path = main (`FromArguments path) diff --git a/infer/src/java/jTrans.ml b/infer/src/java/jTrans.ml index ec05e7e24..0f415bfdd 100644 --- a/infer/src/java/jTrans.ml +++ b/infer/src/java/jTrans.ml @@ -232,7 +232,8 @@ let get_implementation cm = | Javalib.Native -> let cms = cm.Javalib.cm_class_method_signature in let cn, ms = JBasics.cms_split cms in - failwithf "native method %s found in %s@." (JBasics.ms_name ms) (JBasics.cn_name cn) + L.(die InternalError) + "native method %s found in %s@." (JBasics.ms_name ms) (JBasics.cn_name cn) | Javalib.Java t -> (* Sawja doesn't handle invokedynamic, and it will crash with a Match_failure if we give it bytecode with this instruction. hack around this problem by converting all invokedynamic's diff --git a/infer/src/labs/ResourceLeaks.ml b/infer/src/labs/ResourceLeaks.ml index b0327ded8..3434db968 100644 --- a/infer/src/labs/ResourceLeaks.ml +++ b/infer/src/labs/ResourceLeaks.ml @@ -86,7 +86,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct astate | Call (_, Indirect _, _, _, _) -> (* This should never happen in Java. Fail if it does. *) - failwithf "Unexpected indirect call %a" HilInstr.pp instr + L.(die InternalError) "Unexpected indirect call %a" HilInstr.pp instr end (* Create an intraprocedural abstract interpreter from the transfer functions we defined *) @@ -123,5 +123,6 @@ let checker {Callbacks.summary; proc_desc; tenv} : Specs.summary = report post proc_data ; Summary.update_summary (convert_to_summary post) summary | None - -> failwithf "Analyzer failed to compute post for %a" Typ.Procname.pp + -> L.(die InternalError) + "Analyzer failed to compute post for %a" Typ.Procname.pp (Procdesc.get_proc_name proc_data.pdesc) diff --git a/infer/src/quandary/ClangTrace.ml b/infer/src/quandary/ClangTrace.ml index 08e0a3d45..855d8a833 100644 --- a/infer/src/quandary/ClangTrace.ml +++ b/infer/src/quandary/ClangTrace.ml @@ -76,7 +76,7 @@ module SourceKind = struct | Typ.Procname.Block _ -> None | pname - -> failwithf "Non-C++ procname %a in C++ analysis@." Typ.Procname.pp pname + -> L.(die InternalError) "Non-C++ procname %a in C++ analysis" Typ.Procname.pp pname let get_tainted_formals pdesc _ = let get_tainted_formals_ qualified_pname = @@ -211,7 +211,7 @@ module SinkKind = struct | Typ.Procname.Block _ -> None | pname - -> failwithf "Non-C++ procname %a in C++ analysis@." Typ.Procname.pp pname + -> L.(die InternalError) "Non-C++ procname %a in C++ analysis" Typ.Procname.pp pname let pp fmt kind = F.fprintf fmt diff --git a/infer/src/quandary/JavaTaintAnalysis.ml b/infer/src/quandary/JavaTaintAnalysis.ml index e0a80a4f2..9e6c23fcf 100644 --- a/infer/src/quandary/JavaTaintAnalysis.ml +++ b/infer/src/quandary/JavaTaintAnalysis.ml @@ -73,7 +73,7 @@ include TaintAnalysis.Make (struct | pname when BuiltinDecl.is_declared pname -> [] | pname - -> failwithf "Non-Java procname %a in Java analysis@." Typ.Procname.pp pname + -> L.(die InternalError) "Non-Java procname %a in Java analysis" Typ.Procname.pp pname let get_model _ _ _ _ _ = None diff --git a/infer/src/quandary/JavaTrace.ml b/infer/src/quandary/JavaTrace.ml index 10a225d46..04b55398c 100644 --- a/infer/src/quandary/JavaTrace.ml +++ b/infer/src/quandary/JavaTrace.ml @@ -84,7 +84,7 @@ module SourceKind = struct | pname when BuiltinDecl.is_declared pname -> None | pname - -> failwithf "Non-Java procname %a in Java analysis@." Typ.Procname.pp pname + -> L.(die InternalError) "Non-Java procname %a in Java analysis" Typ.Procname.pp pname let get_tainted_formals pdesc tenv = let make_untainted (name, typ) = (name, typ, None) in @@ -148,8 +148,8 @@ module SourceKind = struct | None -> Source.all_formals_untainted pdesc ) | procname - -> failwithf "Non-Java procedure %a where only Java procedures are expected" Typ.Procname.pp - procname + -> L.(die InternalError) + "Non-Java procedure %a where only Java procedures are expected" Typ.Procname.pp procname let pp fmt kind = F.fprintf fmt @@ -284,7 +284,7 @@ module SinkKind = struct | pname when BuiltinDecl.is_declared pname -> None | pname - -> failwithf "Non-Java procname %a in Java analysis@." Typ.Procname.pp pname + -> L.(die InternalError) "Non-Java procname %a in Java analysis" Typ.Procname.pp pname let pp fmt kind = F.fprintf fmt diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index f91650dcf..c17a3d5ea 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -88,8 +88,8 @@ module Make (TaintSpecification : TaintSpec.S) = struct TaintDomain.add_trace actual_ap (TraceDomain.add_source source trace) access_tree | _ -> access_tree - | exception Failure _ - -> failwithf "Bad source specification: index %d out of bounds" index + | exception Failure s + -> L.(die InternalError) "Bad source specification: index %d out of bounds (%s)" index s let endpoints = (lazy (String.Set.of_list (QuandaryConfig.Endpoint.of_json Config.quandary_endpoints))) @@ -266,8 +266,8 @@ module Make (TaintSpecification : TaintSpec.S) = struct | None -> access_tree_acc ) | None - -> failwithf - "Taint is supposed to flow into sink %a at index %d, but the index is out of bounds@\n" + -> L.(die InternalError) + "Taint is supposed to flow into sink %a at index %d, but the index is out of bounds" CallSite.pp callee_site sink_index | _ -> access_tree_acc @@ -493,8 +493,9 @@ module Make (TaintSpecification : TaintSpec.S) = struct exec_write lhs_access_path rhs_exp access_tree |> exec_write dummy_ret_access_path rhs_exp | _ - -> failwithf "Unexpected call to operator= %a in %a" HilInstr.pp instr - Typ.Procname.pp callee_pname ) + -> L.(die InternalError) + "Unexpected call to operator= %a in %a" HilInstr.pp instr Typ.Procname.pp + callee_pname ) | _ -> let model = TaintSpecification.handle_unknown_call callee_pname (Option.map ~f:snd ret_opt) @@ -711,6 +712,6 @@ module Make (TaintSpecification : TaintSpec.S) = struct -> Summary.update_summary (make_summary proc_data access_tree) summary | None -> if Procdesc.Node.get_succs (Procdesc.get_start_node proc_desc) <> [] then - failwith "Couldn't compute post" + L.(die InternalError) "Couldn't compute post" else summary end diff --git a/infer/src/unit/DifferentialFiltersTests.ml b/infer/src/unit/DifferentialFiltersTests.ml index f7c126b66..6db41e9a9 100644 --- a/infer/src/unit/DifferentialFiltersTests.ml +++ b/infer/src/unit/DifferentialFiltersTests.ml @@ -26,7 +26,7 @@ let test_file_renamings_from_json = ~cmp:DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.equal exp (test_output test_input) | Raise exc - -> assert_raises exc (fun () -> test_output test_input) + -> UnitUtils.assert_raises exc (fun () -> test_output test_input) in [ ( "test_file_renamings_from_json_with_good_input" , "[" ^ "{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}," @@ -44,15 +44,16 @@ let test_file_renamings_from_json = []) ) ; ( "test_file_renamings_from_json_with_well_formed_but_unexpected_input" , "{}" - , Raise (Failure "Expected JSON list but got '{}'") ) + , Raise (Logging.InferUserError ("Expected JSON list but got '{}'", "")) ) ; ( "test_file_renamings_from_json_with_well_formed_but_unexpected_value" , "[{\"current\": 1, \"previous\": \"BBB.java\"}]" , Raise - (Failure + (Logging.InferUserError ( "Error parsing file renamings: \"current\" field is not a string" - ^ "\nExpected JSON object of the following form: " - ^ "'{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}', " - ^ "but instead got: '{\"current\":1,\"previous\":\"BBB.java\"}'" )) ) + ^ "\nExpected JSON object of the following form: " + ^ "'{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}', " + ^ "but instead got: '{\"current\":1,\"previous\":\"BBB.java\"}'" + , "" )) ) ; ( "test_file_renamings_from_json_with_malformed_input" , "A" , Raise (Yojson.Json_error "Line 1, bytes 0-1:\nInvalid token 'A'") ) ] diff --git a/infer/src/unit/UnitUtils.ml b/infer/src/unit/UnitUtils.ml new file mode 100644 index 000000000..e5cee4441 --- /dev/null +++ b/infer/src/unit/UnitUtils.ml @@ -0,0 +1,23 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) +let erase_backtrace exn = + match exn with + | Logging.InferUserError (msg, _) + -> Logging.InferUserError (msg, "") + | Logging.InferExternalError (msg, _) + -> Logging.InferExternalError (msg, "") + | Logging.InferInternalError (msg, _) + -> Logging.InferInternalError (msg, "") + | _ + -> exn + +let assert_raises ?msg exn f = + OUnit2.assert_raises ?msg (erase_backtrace exn) (fun () -> + try f () + with exn -> raise (erase_backtrace exn) ) diff --git a/infer/src/unit/UnitUtils.mli b/infer/src/unit/UnitUtils.mli new file mode 100644 index 000000000..e12c6922f --- /dev/null +++ b/infer/src/unit/UnitUtils.mli @@ -0,0 +1,14 @@ +(* + * Copyright (c) 2017 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +val assert_raises : ?msg:string -> exn -> (unit -> 'a) -> unit +(** OUnit2.assert_raises checks that a function raises some exception that's exactly the same as a + reference exception, but some of our internal exceptions contain verbose and flaky data, eg + backtraces. This will normalize such known exceptions by erasing their verbose data. Use this if + you're suffering from OUnit2.assert_raises. *) diff --git a/infer/src/unit/stacktraceTests.ml b/infer/src/unit/stacktraceTests.ml index 0207fb778..e94ae0297 100644 --- a/infer/src/unit/stacktraceTests.ml +++ b/infer/src/unit/stacktraceTests.ml @@ -14,7 +14,8 @@ let tests = let open OUnit2 in let empty_string_test = let empty_string_test_ _ = - assert_raises (Failure "Empty stack trace") (fun () -> Stacktrace.of_string "") + UnitUtils.assert_raises (Logging.InferUserError ("Empty stack trace", "")) (fun () -> + Stacktrace.of_string "" ) in "empty_string" >:: empty_string_test_ in