diff --git a/infer/src/backend/InferPrint.re b/infer/src/backend/InferPrint.re index 6c5184822..5d690786d 100644 --- a/infer/src/backend/InferPrint.re +++ b/infer/src/backend/InferPrint.re @@ -394,7 +394,7 @@ let should_report (issue_kind: Exceptions.err_kind) issue_type error_desc => }; let is_file source_file => - switch (Unix.stat (DB.source_file_to_string source_file)) { + switch (Unix.stat (DB.source_file_to_abs_path source_file)) { | {st_kind: S_REG | S_LNK} => true | _ => false | exception Unix.Unix_error _ => false @@ -496,18 +496,18 @@ let module IssuesJson = { } else { file }; - let make_cpp_models_path_relative file => - if (Utils.string_is_prefix Config.cpp_models_dir file) { + let make_cpp_models_path_relative file => { + let abs_file = DB.source_file_to_abs_path file; + if (Utils.string_is_prefix Config.cpp_models_dir abs_file) { if (Config.debug_mode || Config.debug_exceptions) { - Some ( - DB.source_file_to_rel_path (DB.rel_source_file_from_abs_path Config.cpp_models_dir file) - ) + Some (DB.rel_source_file_from_abs_path Config.cpp_models_dir abs_file) } else { None } } else { Some file - }; + } + }; /** Write bug report in JSON format */ let pp_issues_of_error_log fmt error_filter _ proc_loc_opt procname err_log => { @@ -529,8 +529,7 @@ let module IssuesJson = { | Some proc_loc => proc_loc.Location.file | None => loc.Location.file }; - let file = DB.source_file_to_string source_file; - let file_opt = make_cpp_models_path_relative file; + let file_opt = make_cpp_models_path_relative source_file; if ( in_footprint && error_filter source_file error_desc error_name && @@ -539,7 +538,7 @@ let module IssuesJson = { let kind = Exceptions.err_kind_string ekind; let bug_type = Localise.to_string error_name; let procedure_id = Procname.to_filename procname; - let file = expand_links_under_buck_out (Option.get file_opt); + let file = expand_links_under_buck_out (DB.source_file_to_string (Option.get file_opt)); let json_ml_loc = switch ml_loc_opt { | Some (file, lnum, cnum, enum) when Config.reports_include_ml_loc => diff --git a/infer/src/base/CommandLineOption.ml b/infer/src/base/CommandLineOption.ml index 61635fcbe..af266faf6 100644 --- a/infer/src/base/CommandLineOption.ml +++ b/infer/src/base/CommandLineOption.ml @@ -322,23 +322,6 @@ let mk_string ~default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?exes ?(met ~decode_json:(string_json_decoder ~long) ~mk_spec:(fun set -> Arg.String set) -let mk_path ~default ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc = - mk ~deprecated ~long ?short ~default ?exes ~meta doc - ~default_to_string:(fun s -> s) - ~mk_setter:(fun var str -> - if Filename.is_relative str then ( - (* Replace relative paths with absolute ones on the fly in the args being parsed. This - assumes that [!arg_being_parsed] points at the option name position in - [!args_to_parse], as is the case e.g. when calling [Arg.parse_argv_dynamic - ~current:arg_being_parsed !args_to_parse ...]. *) - let abs_path = Sys.getcwd () // str in - var := abs_path; - (!args_to_parse).(!arg_being_parsed + 1) <- abs_path; - ) else - var := str) - ~decode_json:(string_json_decoder ~long) - ~mk_spec:(fun set -> Arg.String set) - let mk_string_opt ?default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc = let default_to_string = function Some s -> s | None -> "" in let f s = Some (f s) in @@ -352,6 +335,43 @@ let mk_string_list ?(default=[]) ?(f=fun s -> s) ~decode_json:(list_json_decoder (string_json_decoder ~long)) ~mk_spec:(fun set -> Arg.String set) +let mk_path_helper ~setter ~default_to_string + ~default ~deprecated ~long ~short ~exes ~meta doc = + let normalize_path_in_args_being_parsed str = + if Filename.is_relative str then ( + (* Replace relative paths with absolute ones on the fly in the args being parsed. This assumes + that [!arg_being_parsed] points at the option name position in [!args_to_parse], as is the + case e.g. when calling + [Arg.parse_argv_dynamic ~current:arg_being_parsed !args_to_parse ...]. *) + let abs_path = filename_to_absolute str in + (!args_to_parse).(!arg_being_parsed + 1) <- abs_path; + abs_path + ) else + str in + mk ~deprecated ~long ?short ~default ?exes ~meta doc + ~default_to_string + ~mk_setter:(fun var str -> + let abs_path = normalize_path_in_args_being_parsed str in + setter var abs_path) + ~decode_json:(string_json_decoder ~long) + ~mk_spec:(fun set -> Arg.String set) + +let mk_path ~default ?(deprecated=[]) ~long ?short ?exes ?(meta="path") = + mk_path_helper ~setter:(fun var x -> var := x) ~default_to_string:(fun s -> s) + ~default ~deprecated ~long ~short ~exes ~meta + +let mk_path_opt ?default ?(deprecated=[]) ~long ?short ?exes ?(meta="path") = + mk_path_helper + ~setter:(fun var x -> var := Some x) + ~default_to_string:(function Some s -> s | None -> "") + ~default ~deprecated ~long ~short ~exes ~meta + +let mk_path_list ?(default=[]) ?(deprecated=[]) ~long ?short ?exes ?(meta="path") = + mk_path_helper + ~setter:(fun var x -> var := x :: !var) + ~default_to_string:(String.concat ", ") + ~default ~deprecated ~long ~short ~exes ~meta + let mk_symbol ~default ~symbols ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc = let strings = IList.map fst symbols in let sym_to_str = IList.map (fun (x,y) -> (y,x)) symbols in diff --git a/infer/src/base/CommandLineOption.mli b/infer/src/base/CommandLineOption.mli index ac07dfcec..4c0887a69 100644 --- a/infer/src/base/CommandLineOption.mli +++ b/infer/src/base/CommandLineOption.mli @@ -66,8 +66,6 @@ val mk_float : default:float -> float ref t val mk_string : default:string -> ?f:(string -> string) -> string ref t -val mk_path : default:string -> string ref t - val mk_string_opt : ?default:string -> ?f:(string -> string) -> string option ref t (** [mk_string_list] defines a [string list ref], initialized to [[]] unless overridden by @@ -76,6 +74,16 @@ val mk_string_opt : ?default:string -> ?f:(string -> string) -> string option re val mk_string_list : ?default:string list -> ?f:(string -> string) -> string list 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 *) +val mk_path : default:string -> string ref t + +(** analogous of [mk_string_opt] with the extra feature of [mk_path] *) +val mk_path_opt : ?default:string -> string option ref t + +(** analogous of [mk_string_list] with the extra feature of [mk_path] *) +val mk_path_list : ?default:string list -> string list ref t + (** [mk_symbol long symbols] defines a command line flag [--long ] where [(,_)] is an element of [symbols]. *) val mk_symbol : default:'a -> symbols:(string * 'a) list -> 'a ref t diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index e38b58a5f..8b257539b 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -411,11 +411,7 @@ let init_work_dir, is_originator = (** Resolve relative paths passed as command line options, i.e., with respect to the working directory of the initial invocation of infer. *) -let resolve path = - if Filename.is_relative path then - init_work_dir // path - else - path +let resolve = filename_to_absolute (** Command Line options *) @@ -424,13 +420,12 @@ let resolve path = let inferconfig_home = let all_exes = IList.map snd CLOpt.exes in - CLOpt.mk_string_opt ~long:"inferconfig-home" + CLOpt.mk_path_opt ~long:"inferconfig-home" ~exes:all_exes ~meta:"dir" "Path to the .inferconfig file" and project_root = - CLOpt.mk_string ~deprecated:["project_root"; "-project_root"] ~long:"project-root" ~short:"pr" + CLOpt.mk_path ~deprecated:["project_root"; "-project_root"] ~long:"project-root" ~short:"pr" ~default:init_work_dir - ~f:resolve ~exes:CLOpt.[Analyze;Clang;Java;Print;Toplevel] ~meta:"dir" "Specify the root directory of the project" @@ -587,7 +582,7 @@ and array_level = - 2 = assumes that all heap dereferences via array indexing and pointer \ arithmetic are correct" and ast_file = - CLOpt.mk_string_opt ~long:"ast-file" ~short:"ast" + CLOpt.mk_path_opt ~long:"ast-file" ~short:"ast" ~meta:"file" "AST file for the translation" and blacklist = @@ -607,45 +602,45 @@ and buck_build_args = "Pass values as command-line arguments to invocations of `buck build` (Buck flavors only)" and buck_out = - CLOpt.mk_string_opt ~long:"buck-out" + CLOpt.mk_path_opt ~long:"buck-out" ~exes:CLOpt.[Toplevel] ~meta:"dir" "Specify the root directory of buck-out" and bugs_csv = - CLOpt.mk_string_opt ~deprecated:["bugs"] ~long:"issues-csv" + CLOpt.mk_path_opt ~deprecated:["bugs"] ~long:"issues-csv" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write a list of issues in CSV format to a file" and bugs_json = - CLOpt.mk_string_opt ~deprecated:["bugs_json"] ~long:"issues-json" + CLOpt.mk_path_opt ~deprecated:["bugs_json"] ~long:"issues-json" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write a list of issues in JSON format to a file" and bugs_tests = - CLOpt.mk_string_opt ~long:"issues-tests" + CLOpt.mk_path_opt ~long:"issues-tests" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write a list of issues in a format suitable for tests to a file" and bugs_txt = - CLOpt.mk_string_opt ~deprecated:["bugs_txt"] ~long:"issues-txt" + CLOpt.mk_path_opt ~deprecated:["bugs_txt"] ~long:"issues-txt" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write a list of issues in TXT format to a file" and bugs_xml = - CLOpt.mk_string_opt ~deprecated:["bugs_xml"] ~long:"issues-xml" + CLOpt.mk_path_opt ~deprecated:["bugs_xml"] ~long:"issues-xml" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write a list of issues in XML format to a file" and calls_csv = - CLOpt.mk_string_opt ~deprecated:["calls"] ~long:"calls-csv" + CLOpt.mk_path_opt ~deprecated:["calls"] ~long:"calls-csv" ~exes:CLOpt.[Toplevel;Print] ~meta:"file" "Write individual calls in CSV format to a file" and changed_files_index = - CLOpt.mk_string_opt ~long:"changed-files-index" ~exes:CLOpt.[Toplevel] ~meta:"file" + CLOpt.mk_path_opt ~long:"changed-files-index" ~exes:CLOpt.[Toplevel] ~meta:"file" "Specify the file containing the list of files from which reactive analysis should start" and check_duplicate_symbols = @@ -680,11 +675,11 @@ and checkers_repeated_calls = "Check for repeated calls" and clang_biniou_file = - CLOpt.mk_string_opt ~long:"clang-biniou-file" ~exes:CLOpt.[Clang] ~meta:"file" + CLOpt.mk_path_opt ~long:"clang-biniou-file" ~exes:CLOpt.[Clang] ~meta:"file" "Specify a file containing the AST of the program, in biniou format" and clang_compilation_database = - CLOpt.mk_string_opt ~long:"clang-compilation-database" + CLOpt.mk_path_opt ~long:"clang-compilation-database" ~exes:CLOpt.[BuckCompilationDatabase] ~meta:"file" "Specify a json file containing a clang compilation database to be used for the analysis" @@ -705,7 +700,7 @@ and _ = ~meta:"path" "Specify where to find user class files and annotation processors" and cluster = - CLOpt.mk_string_opt ~deprecated:["cluster"] ~long:"cluster" + CLOpt.mk_path_opt ~deprecated:["cluster"] ~long:"cluster" ~meta:"file" "Specify a .cluster file to be analyzed" (** Continue the capture for reactive mode: @@ -887,7 +882,7 @@ and failures_allowed = "Fail if at least one of the translations fails (clang only)" and fcp_apple_clang = - CLOpt.mk_string_opt ~long:"fcp-apple-clang" + CLOpt.mk_path_opt ~long:"fcp-apple-clang" ~meta:"path" "Specify the path to Apple Clang" and fcp_syntax_only = @@ -924,7 +919,7 @@ and headers = "Analyze code in header files" and infer_cache = - CLOpt.mk_string_opt ~deprecated:["infer_cache"; "-infer_cache"] ~long:"infer-cache" ~f:resolve + CLOpt.mk_path_opt ~deprecated:["infer_cache"; "-infer_cache"] ~long:"infer-cache" ~meta:"dir" "Select a directory to contain the infer cache (Buck and Java only)" and iterations = @@ -934,7 +929,7 @@ and iterations = symbolic operations and a multiple of seconds of elapsed time" and java_jar_compiler = - CLOpt.mk_string_opt + CLOpt.mk_path_opt ~long:"java-jar-compiler" ~exes:CLOpt.[Java] ~meta:"path" "Specifify the Java compiler jar used to generate the bytecode" @@ -950,12 +945,12 @@ and join_cond = - 1 = use the least aggressive join for preconditions" and latex = - CLOpt.mk_string_opt ~deprecated:["latex"] ~long:"latex" + CLOpt.mk_path_opt ~deprecated:["latex"] ~long:"latex" ~meta:"file" "Write a latex report of the analysis results to a file" and linters_def_file = - CLOpt.mk_string_opt ~long:"linters-def-file" ~exes:CLOpt.[Clang] + CLOpt.mk_path_opt ~long:"linters-def-file" ~exes:CLOpt.[Clang] ~meta:"file" "Specify the file containing linters definition" and load_average = @@ -965,13 +960,13 @@ and load_average = make only)" and load_results = - CLOpt.mk_string_opt ~deprecated:["load_results"] ~long:"load-results" + CLOpt.mk_path_opt ~deprecated:["load_results"] ~long:"load-results" ~exes:CLOpt.[Print] ~meta:"file.iar" "Load analysis results from Infer Analysis Results file file.iar" (** name of the makefile to create with clusters and dependencies *) and makefile = - CLOpt.mk_string ~deprecated:["makefile"] ~long:"makefile" ~default:"" + CLOpt.mk_path ~deprecated:["makefile"] ~long:"makefile" ~default:"" ~meta:"file" "" and margin = @@ -995,7 +990,7 @@ and ml_buckets = ~symbols:ml_bucket_symbols and models_file = - CLOpt.mk_string_opt ~deprecated:["models"] ~long:"models" ~f:resolve + CLOpt.mk_path_opt ~deprecated:["models"] ~long:"models" ~exes:CLOpt.[Analyze;Java] ~meta:"jar file" "Specify a jar file containing the Java models" and models_mode = @@ -1003,7 +998,7 @@ and models_mode = "Mode for analyzing the models" and modified_targets = - CLOpt.mk_string_opt ~deprecated:["modified_targets"] ~long:"modified-targets" + CLOpt.mk_path_opt ~deprecated:["modified_targets"] ~long:"modified-targets" ~meta:"file" "Read the file of Buck targets modified since the last analysis" and monitor_prop_size = @@ -1028,7 +1023,7 @@ and optimistic_cast = "Allow cast of undefined values" and out_file = - CLOpt.mk_string ~deprecated:["out_file"] ~long:"out-file" ~default:"" + CLOpt.mk_path ~deprecated:["out_file"] ~long:"out-file" ~default:"" ~meta:"file" "Specify the file for the non-error logs of the analyzer" and ( @@ -1065,11 +1060,11 @@ and print_using_diff = "Highlight the difference w.r.t. the previous prop when printing symbolic execution debug info" and procs_csv = - CLOpt.mk_string_opt ~deprecated:["procs"] ~long:"procs-csv" + CLOpt.mk_path_opt ~deprecated:["procs"] ~long:"procs-csv" ~meta:"file" "Write statistics for each procedure in CSV format to a file" and procs_xml = - CLOpt.mk_string_opt ~deprecated:["procs_xml"] ~long:"procs-xml" + CLOpt.mk_path_opt ~deprecated:["procs_xml"] ~long:"procs-xml" ~meta:"file" "Write statistics for each procedure in XML format to a file (as a path relative to \ --results-dir)" @@ -1089,7 +1084,7 @@ and reactive = "Reactive mode: the analysis starts from the files captured since the `infer` command started" and report = - CLOpt.mk_string_opt ~deprecated:["report"] ~long:"report" + CLOpt.mk_path_opt ~deprecated:["report"] ~long:"report" ~meta:"file" "Write a report of the analysis results to a file" and report_custom_error = @@ -1103,7 +1098,7 @@ and results_dir = ~meta:"dir" "Write results and internal files in the specified directory" and save_results = - CLOpt.mk_string_opt ~deprecated:["save_results"] ~long:"save-results" + CLOpt.mk_path_opt ~deprecated:["save_results"] ~long:"save-results" ~meta:"file.iar" "Save analysis results to Infer Analysis Results file file.iar" and seconds_per_iteration = @@ -1137,7 +1132,7 @@ and spec_abs_level = and specs_library = let specs_library = - CLOpt.mk_string_list ~long:"specs-library" ~short:"lib" ~f:resolve + CLOpt.mk_path_list ~long:"specs-library" ~short:"lib" ~meta:"dir|jar" "Search for .spec files in given directory or jar file" in let _ = (* Given a filename with a list of paths, convert it into a list of string iff they are @@ -1163,13 +1158,13 @@ and specs_library = specs_library and stacktrace = - CLOpt.mk_string_opt ~long:"stacktrace" ~short:"st" ~f:resolve ~exes:CLOpt.[Toplevel] + CLOpt.mk_path_opt ~long:"stacktrace" ~short:"st" ~exes:CLOpt.[Toplevel] ~meta:"file" "File path containing a json-encoded Java crash stacktrace. Used to guide the \ analysis (only with '-a crashcontext'). See \ tests/codetoanalyze/java/crashcontext/*.json for examples of the expected format." and stacktraces_dir = - CLOpt.mk_string_opt ~long:"stacktraces-dir" ~f:resolve ~exes:CLOpt.[Toplevel] + CLOpt.mk_path_opt ~long:"stacktraces-dir" ~exes:CLOpt.[Toplevel] ~meta:"dir" "Directory path containing multiple json-encoded Java crash stacktraces. \ Used to guide the analysis (only with '-a crashcontext'). See \ tests/codetoanalyze/java/crashcontext/*.json for examples of the expected format." @@ -1183,7 +1178,7 @@ and subtype_multirange = (* Path to list of collected @SuppressWarnings annotations *) and suppress_warnings_out = - CLOpt.mk_string_opt ~deprecated:["suppress_warnings_out"] ~long:suppress_warnings_annotations_long + CLOpt.mk_path_opt ~deprecated:["suppress_warnings_out"] ~long:suppress_warnings_annotations_long ~meta:"path" "" and svg = @@ -1240,7 +1235,7 @@ and use_compilation_database = (** Set the path to the javac verbose output *) and verbose_out = - CLOpt.mk_string ~deprecated:["verbose_out"] ~long:"verbose-out" ~default:"" + CLOpt.mk_path ~deprecated:["verbose_out"] ~long:"verbose-out" ~default:"" ~meta:"file" "" and version = @@ -1275,7 +1270,7 @@ and worklist_mode = var and xcode_developer_dir = - CLOpt.mk_string_opt ~long:"xcode-developer-dir" + CLOpt.mk_path_opt ~long:"xcode-developer-dir" ~exes:CLOpt.[Toplevel] ~meta:"XCODE_DEVELOPER_DIR" "Specify the path to Xcode developer directory (Buck flavors only)" diff --git a/infer/src/checkers/Stacktrace.ml b/infer/src/checkers/Stacktrace.ml index 0de241b30..4caf82e56 100644 --- a/infer/src/checkers/Stacktrace.ml +++ b/infer/src/checkers/Stacktrace.ml @@ -88,13 +88,12 @@ let of_string s = make exception_name parsed | [] -> failwith "Empty stack trace" -let of_json json = +let of_json filename json = let exception_name_key = "exception_type" in let frames_key = "stack_trace" in let extract_json_member key = match Yojson.Basic.Util.member key json with - | `Null -> failwith ("Missing key in supplied JSON \ - data: " ^ key) + | `Null -> failwithf "Missing key in supplied JSON data: %s (in file %s)" key filename | item -> item in let exception_name = Yojson.Basic.Util.to_string (extract_json_member exception_name_key) in @@ -107,7 +106,8 @@ let of_json json = make exception_name frames let of_json_file filename = - match Utils.read_optional_json_file filename with - | Ok json -> of_json json - | Error msg -> failwith (Printf.sprintf "Could not read or parse the supplied JSON \ - stacktrace file %s :\n %s" filename msg) + 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