diff --git a/.gitignore b/.gitignore index 7d46ae39b..a1b5640e2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ duplicates.txt /infer/tests/build_systems/differential_*/**/Diff*.java /infer/tests/build_systems/differential_*/infer-current /infer/tests/build_systems/differential_*/infer-previous +/infer/tests/build_systems/diff/src /_release # generated by oUnit diff --git a/Makefile b/Makefile index f1781e466..209a8bb5e 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ BUILD_SYSTEMS_TESTS += \ clang_with_M_flag \ clang_with_MD_flag \ delete_results_dir \ + diff \ fail_on_issue \ j1 \ linters \ diff --git a/infer/src/backend/InferPrint.ml b/infer/src/backend/InferPrint.ml index 6ace6f21f..6ed8b8525 100644 --- a/infer/src/backend/InferPrint.ml +++ b/infer/src/backend/InferPrint.ml @@ -322,7 +322,9 @@ let potential_exception_message = "potential exception at line" module IssuesJson = struct let is_first_item = ref true - let pp_json_open fmt () = F.fprintf fmt "[@?" + let pp_json_open fmt () = + is_first_item := true ; + F.fprintf fmt "[@?" let pp_json_close fmt () = F.fprintf fmt "]@\n@?" diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index 9535e3cf1..175d4d5cd 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -73,6 +73,8 @@ let setup_results_dir () = -> assert_results_dir "have you run capture before?" | Clang | Report | ReportDiff -> create_results_dir () + | Diff + -> remove_results_dir () ; create_results_dir () | Capture | Compile | Run -> let driver_mode = Lazy.force Driver.mode_from_command_line in if not @@ -124,3 +126,5 @@ let () = ~previous_report:Config.report_previous | Capture | Compile | Run -> run (Lazy.force Driver.mode_from_command_line) + | Diff + -> Diff.diff (Lazy.force Driver.mode_from_command_line) diff --git a/infer/src/base/CommandDoc.ml b/infer/src/base/CommandDoc.ml index f41f6e4dc..b8bd9b1db 100644 --- a/infer/src/base/CommandDoc.ml +++ b/infer/src/base/CommandDoc.ml @@ -23,6 +23,7 @@ let command_to_name = [ (Analyze, "analyze") ; (Capture, "capture") ; (Compile, "compile") + ; (Diff, "diff") ; (Report, "report") ; (ReportDiff, "reportdiff") ; (Run, "run") ] @@ -125,6 +126,13 @@ let compile = ] ~see_also:CLOpt.([Capture]) +let diff = + mk_command_doc ~title:"Infer Differential Analysis of a Project" + ~short_description:"Report the difference between two versions of a project" + ~synopsis:"$(b,infer) $(b,diff) $(i,[options])" + ~description:[`P "EXPERIMENTAL AND IN NO WAY READY TO USE"] + ~see_also:CLOpt.([ReportDiff; Run]) + let infer = mk_command_doc ~title:"Infer Static Analyzer" ~short_description:"static analysis for Java and C/C++/Objective-C/Objective-C++" @@ -258,6 +266,7 @@ let command_to_data = [ mk Analyze analyze ; mk Capture capture ; mk Compile compile + ; mk Diff diff ; mk Report report ; mk ReportDiff reportdiff ; mk Run run ] diff --git a/infer/src/base/CommandLineOption.ml b/infer/src/base/CommandLineOption.ml index 909b75fa1..9aa3da250 100644 --- a/infer/src/base/CommandLineOption.ml +++ b/infer/src/base/CommandLineOption.ml @@ -89,6 +89,7 @@ type command = | Capture | Clang | Compile + | Diff | Report | ReportDiff | Run @@ -96,7 +97,7 @@ type command = let equal_command = [%compare.equal : command] -let all_commands = [Analyze; Capture; Clang; Compile; Report; ReportDiff; Run] +let all_commands = [Analyze; Capture; Clang; Compile; Diff; Report; ReportDiff; Run] type command_doc = { title: Cmdliner.Manpage.title diff --git a/infer/src/base/CommandLineOption.mli b/infer/src/base/CommandLineOption.mli index 4e3fbe08b..6d4e08ba7 100644 --- a/infer/src/base/CommandLineOption.mli +++ b/infer/src/base/CommandLineOption.mli @@ -33,6 +33,7 @@ type command = | Compile (** set up the infer environment then run the compilation commands without capturing the source files *) + | Diff (** orchestrate a diff analysis *) | Report (** post-process infer results and reports *) | ReportDiff (** compute the difference of two infer reports *) | Run (** orchestrate the capture, analysis, and reporting of a compilation command *) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 53e0952b1..2d5f7755a 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -357,7 +357,7 @@ let startup_action = match initial_command with | Some Clang -> NoParse - | None | Some (Analyze | Capture | Compile | Report | ReportDiff | Run) + | None | Some (Analyze | Capture | Compile | Diff | Report | ReportDiff | Run) -> InferCommand let exe_usage = @@ -411,7 +411,7 @@ let () = -> assert false (* filtered out *) | Report -> `Add - | Analyze | Capture | Compile | ReportDiff | Run + | Analyze | Capture | Compile | Diff | ReportDiff | Run -> `Reject in (* make sure we generate doc for all the commands we know about *) @@ -759,6 +759,12 @@ and copy_propagation = CLOpt.mk_bool ~deprecated:["copy-propagation"] ~long:"copy-propagation" "Perform copy-propagation on the IR" +and current_to_previous_script = + CLOpt.mk_string_opt ~long:"current-to-previous-script" + ~in_help:CLOpt.([(Diff, manual_generic)]) + ~meta:"shell" + "Specify a script to checkout a previous version of the project to compare against, assuming we are on the current version already." + and cxx, cxx_infer_headers = let cxx_infer_headers = CLOpt.mk_bool ~long:"cxx-infer-headers" ~default:true @@ -1255,6 +1261,12 @@ and precondition_stats = CLOpt.mk_bool ~deprecated:["precondition_stats"] ~long:"precondition-stats" "Print stats about preconditions to standard output" +and previous_to_current_script = + CLOpt.mk_string_opt ~long:"previous-to-current-script" + ~in_help:CLOpt.([(Diff, manual_generic)]) + ~meta:"shell" + "Specify a script to checkout the current version of the project. The project is supposed to already be at that current version when running $(b,infer diff); the script is used after having analyzed the current and previous versions of the project, to restore the project to the current version." + and print_active_checkers = CLOpt.mk_bool ~long:"print-active-checkers" ~in_help:CLOpt.([(Analyze, manual_generic)]) @@ -1813,11 +1825,7 @@ and compute_analytics = !compute_analytics and continue_capture = !continue -and linter = !linter - -and default_linters = !default_linters - -and linters_ignore_clang_failures = !linters_ignore_clang_failures +and current_to_previous_script = !current_to_previous_script and copy_propagation = !copy_propagation @@ -1839,6 +1847,8 @@ and debug_exceptions = !debug_exceptions and debug_mode = !debug +and default_linters = !default_linters + and dependency_mode = !dependencies and developer_mode = !developer_mode @@ -1925,12 +1935,16 @@ and join_cond = !join_cond and latex = !latex +and linter = !linter + and linters_def_file = !linters_def_file and linters_def_folder = !linters_def_folder and linters_developer_mode = !linters_developer_mode +and linters_ignore_clang_failures = !linters_ignore_clang_failures + and load_average = match !load_average with None when !buck -> Some (float_of_int ncpu) | _ -> !load_average @@ -1982,6 +1996,8 @@ and pmd_xml = !pmd_xml and precondition_stats = !precondition_stats +and previous_to_current_script = !previous_to_current_script + and printf_args = !printf_args and print_active_checkers = !print_active_checkers diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 85833f256..0accd7543 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -322,16 +322,14 @@ val compute_analytics : bool val continue_capture : bool -val default_linters : bool - -val linters_ignore_clang_failures : bool - val copy_propagation : bool val crashcontext : bool val create_harness : bool +val current_to_previous_script : string option + val cxx : bool val cxx_infer_headers : bool @@ -346,6 +344,8 @@ val debug_exceptions : bool val debug_mode : bool +val default_linters : bool + val dependency_mode : bool val developer_mode : bool @@ -469,6 +469,8 @@ val linters_def_folder : string list val linters_developer_mode : bool +val linters_ignore_clang_failures : bool + val load_analysis_results : string option val log_file : string @@ -502,6 +504,8 @@ val pmd_xml : bool val precondition_stats : bool +val previous_to_current_script : string option + val print_active_checkers : bool val print_builtins : bool diff --git a/infer/src/integration/Diff.ml b/infer/src/integration/Diff.ml new file mode 100644 index 000000000..e8c1f4334 --- /dev/null +++ b/infer/src/integration/Diff.ml @@ -0,0 +1,65 @@ +(* + * 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 +module L = Logging + +type revision = Current | Previous + +let string_of_revision = function Current -> "current" | Previous -> "previous" + +let pp_revision f r = F.fprintf f "%s" (string_of_revision r) + +let checkout revision = + let script_opt = + match revision with + | Current + -> Config.previous_to_current_script + | Previous + -> Config.current_to_previous_script + in + match script_opt with + | None + -> L.(die UserError) + "Please specify a script to checkout the %a revision of your project using --checkout-%a