diff --git a/.gitignore b/.gitignore index 253c2e43b..86e579b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ duplicates.txt /infer/tests/build_systems/differential_*/infer-current /infer/tests/build_systems/differential_*/infer-previous /infer/tests/build_systems/diff/src +/infer/tests/build_systems/diff_*/src /_release # generated by oUnit diff --git a/Makefile b/Makefile index 6dd642885..205c0800c 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ BUILD_SYSTEMS_TESTS += \ clang_with_MD_flag \ delete_results_dir \ diff \ + diff_gen_build_script \ fail_on_issue \ j1 \ linters \ diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 2cee5249e..3ec3ef33c 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1126,6 +1126,12 @@ and frontend_stats = CLOpt.mk_bool ~deprecated:["fs"] ~deprecated_no:["nfs"] ~long:"frontend-stats" "Output statistics about the capture phase to *.o.astlog (clang only)" +and gen_previous_build_command_script = + CLOpt.mk_string_opt ~long:"gen-previous-build-command-script" + ~in_help:CLOpt.([(Diff, manual_generic)]) + ~meta:"shell" + "Specify a script that outputs the build command to capture in the previous version of the project. The script should output the command on stdout. For example \"echo make\"." + and generated_classes = CLOpt.mk_path_opt ~long:"generated-classes" ~in_help:CLOpt.([(Capture, manual_java)]) @@ -1862,6 +1868,8 @@ and bugs_csv = !bugs_csv and frontend_tests = !frontend_tests +and gen_previous_build_command_script = !gen_previous_build_command_script + and generated_classes = !generated_classes and bugs_tests = !bugs_tests diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 9d7d1560d..30c1ea631 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -405,6 +405,8 @@ val frontend_tests : bool val frontend_stats : bool +val gen_previous_build_command_script : string option + val generated_classes : string option val headers : bool diff --git a/infer/src/integration/Diff.ml b/infer/src/integration/Diff.ml index 162de6680..edf282ac9 100644 --- a/infer/src/integration/Diff.ml +++ b/infer/src/integration/Diff.ml @@ -43,20 +43,43 @@ let save_report revision = L.progress "Results for the %a revision stored in %s@\n" pp_revision revision report_name ; report_name +let gen_previous_driver_mode script = + let output, exit_or_signal = Utils.with_process_in script In_channel.input_lines in + match exit_or_signal with + | Error _ as status + -> L.(die UserError) + "*** command failed:@\n*** %s@\n*** %s@." script (Unix.Exit_or_signal.to_string_hum status) + | Ok () + -> (* FIXME(t15553258): this won't work if the build command has arguments that contain spaces. In that case the user should be able to use an argfile for the build command instead, so not critical to fix. *) + let command = List.concat_map ~f:(String.split ~on:' ') output in + L.environment_info "Build command for the previous project version: '%s'@\n%!" + (String.concat ~sep:" " command) ; + Driver.mode_of_build_command command + +let delete_capture_and_analysis_artifacts () = + let dirs_to_delete = + List.map ~f:(Filename.concat Config.results_dir) + Config.([attributes_dir_name; captured_dir_name; specs_dir_name]) + in + List.iter ~f:Utils.rmtree dirs_to_delete ; List.iter ~f:Unix.mkdir_p dirs_to_delete ; () + let diff driver_mode = Driver.run_prologue driver_mode ; let changed_files = Driver.read_config_changed_files () in - (* TODO(t15553258) run gen-build script if specified *) - (* run capture *) Driver.capture driver_mode ~changed_files ; Driver.analyze_and_report driver_mode ~changed_files ; let current_report = Some (save_report Current) in + (* Some files in the current checkout may be deleted in the old checkout. If we kept the results of the previous capture and analysis around, we would report issues on these files again in the previous checkout, which is wrong. Do not do anything too smart for now and just delete all results from the analysis of the current checkout. *) + delete_capture_and_analysis_artifacts () ; (* TODO(t15553258) bail if nothing to analyze (configurable, some people might care about bugs fixed more than about time to analyze) *) checkout Previous ; - (* TODO(t15553258) run gen-build script if specified *) - Driver.capture driver_mode ~changed_files ; - Driver.analyze_and_report driver_mode ~changed_files ; + let previous_driver_mode = + Option.value_map ~default:driver_mode ~f:gen_previous_driver_mode + Config.gen_previous_build_command_script + in + Driver.capture previous_driver_mode ~changed_files ; + Driver.analyze_and_report previous_driver_mode ~changed_files ; checkout Current ; let previous_report = Some (save_report Previous) in (* compute differential *) diff --git a/infer/src/integration/Driver.mli b/infer/src/integration/Driver.mli index 70acdf43c..5b32bc99f 100644 --- a/infer/src/integration/Driver.mli +++ b/infer/src/integration/Driver.mli @@ -32,6 +32,9 @@ val equal_mode : mode -> mode -> bool val mode_from_command_line : mode Lazy.t (** driver mode computed from the command-line arguments and settings in Config *) +val mode_of_build_command : string list -> mode +(** driver mode computed from the build command alone, eg [["buck"; "build"; ...]] gives [PythonCapture (BBuck, ["buck"; "build"; ...])] *) + val run_prologue : mode -> unit (** prepare the environment for running the given mode *) diff --git a/infer/tests/build_systems/codetoanalyze/some_bugs.c b/infer/tests/build_systems/codetoanalyze/some_bugs.c index fc05284f9..5133ad75e 100644 --- a/infer/tests/build_systems/codetoanalyze/some_bugs.c +++ b/infer/tests/build_systems/codetoanalyze/some_bugs.c @@ -14,7 +14,9 @@ void test1() { *s1 = 42; } -void test2() { +void nullify(int* x) { *x = 0; } + +void test2(int x) { int* s2 = NULL; - *s2 = 42; + nullify(s2); } diff --git a/infer/tests/build_systems/diff/Makefile b/infer/tests/build_systems/diff/Makefile index 4d66d2bb9..cb20dce22 100644 --- a/infer/tests/build_systems/diff/Makefile +++ b/infer/tests/build_systems/diff/Makefile @@ -9,6 +9,8 @@ TESTS_DIR = ../.. INFER_OUT = infer-out include $(TESTS_DIR)/diff.make +CLEAN_EXTRA = src + SRC_DIR = $(CURDIR)/../codetoanalyze INFER_OPTIONS = \ --previous-to-current-script '$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c' \ @@ -19,7 +21,8 @@ INFER_OPTIONS = \ SOURCES = $(SRC_DIR)/some_bugs.c $(SRC_DIR)/some_different_bugs.c -$(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt +$(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt \ + $(MAKEFILE_LIST) $(QUIET)$(MKDIR_P) src $(QUIET)$(COPY) $(SRC_DIR)/some_bugs.c src/hello.c $(QUIET)$(call silent_on_success,Running diff analysis in $(TEST_REL_DIR),\ diff --git a/infer/tests/build_systems/diff/introduced.exp b/infer/tests/build_systems/diff/introduced.exp index 05eb02609..a68bee185 100644 --- a/infer/tests/build_systems/diff/introduced.exp +++ b/infer/tests/build_systems/diff/introduced.exp @@ -1 +1 @@ -src/hello.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2()] +src/hello.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2(),start of procedure nullify()] diff --git a/infer/tests/build_systems/diff_gen_build_script/Makefile b/infer/tests/build_systems/diff_gen_build_script/Makefile new file mode 100644 index 000000000..73218ea6c --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/Makefile @@ -0,0 +1,32 @@ +# 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. + +TESTS_DIR = ../.. +INFER_OUT = infer-out +include $(TESTS_DIR)/diff.make + +CLEAN_EXTRA = src + +SRC_DIR = $(CURDIR)/../codetoanalyze +INFER_OPTIONS = \ + --previous-to-current-script 'true' \ + --current-to-previous-script '$(COPY) $(SRC_DIR)/some_bugs.c src/' \ + --gen-previous-build-command-script 'echo "clang -c src/some_bugs.c"' \ + --changed-files-index changed_files.txt \ + --file-renamings file_renamings.json \ + --report-hook '/bin/true' \ + -- clang -c src/some_different_bugs.c + +SOURCES = $(SRC_DIR)/some_bugs.c $(SRC_DIR)/some_different_bugs.c + +$(INFER_OUT)/differential/introduced.json: $(SOURCES) $(CLANG_DEPS) changed_files.txt \ + file_renamings.json $(MAKEFILE_LIST) + $(QUIET)$(MKDIR_P) src + $(QUIET)$(COPY) $(SRC_DIR)/some_different_bugs.c src/ + $(QUIET)$(call silent_on_success,Running diff analysis in $(TEST_REL_DIR),\ + $(INFER_BIN) --no-keep-going -o $(INFER_OUT) --project-root $(CURDIR) diff \ + $(INFER_OPTIONS)) diff --git a/infer/tests/build_systems/diff_gen_build_script/changed_files.txt b/infer/tests/build_systems/diff_gen_build_script/changed_files.txt new file mode 100644 index 000000000..671f7f32b --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/changed_files.txt @@ -0,0 +1,2 @@ +src/some_bugs.c +src/some_different_bugs.c diff --git a/infer/tests/build_systems/diff_gen_build_script/file_renamings.json b/infer/tests/build_systems/diff_gen_build_script/file_renamings.json new file mode 100644 index 000000000..a119a5868 --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/file_renamings.json @@ -0,0 +1,6 @@ +[ + { + "previous": "src/some_bugs.c", + "current": "src/some_different_bugs.c" + } +] diff --git a/infer/tests/build_systems/diff_gen_build_script/fixed.exp b/infer/tests/build_systems/diff_gen_build_script/fixed.exp new file mode 100644 index 000000000..8d8ac624d --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/fixed.exp @@ -0,0 +1 @@ +src/some_bugs.c, test2, 2, NULL_DEREFERENCE, [start of procedure test2(),start of procedure nullify()] diff --git a/infer/tests/build_systems/diff_gen_build_script/introduced.exp b/infer/tests/build_systems/diff_gen_build_script/introduced.exp new file mode 100644 index 000000000..e0c438e80 --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/introduced.exp @@ -0,0 +1 @@ +src/some_different_bugs.c, test3, 3, MEMORY_LEAK, [start of procedure test3(),Condition is true] diff --git a/infer/tests/build_systems/diff_gen_build_script/preexisting.exp b/infer/tests/build_systems/diff_gen_build_script/preexisting.exp new file mode 100644 index 000000000..9c47d0747 --- /dev/null +++ b/infer/tests/build_systems/diff_gen_build_script/preexisting.exp @@ -0,0 +1 @@ +src/some_different_bugs.c, test1, 2, NULL_DEREFERENCE, [start of procedure test1()]