Clang Diff Determinator

Reviewed By: jvillard

Differential Revision: D14722626

fbshipit-source-id: 074ef2413
master
Martin Trojer 6 years ago committed by Facebook Github Bot
parent 67cd0e1cd7
commit 52fd4c50de

17
.gitignore vendored

@ -24,22 +24,23 @@ duplicates.txt
*.ast.sh *.ast.sh
*.ast.bdump *.ast.bdump
*.ast.biniou *.ast.biniou
/infer/tests/build_systems/buck_flavors_deterministic/capture_hash-*.sha
/infer/tests/build_systems/buck_flavors_diff/src/hello.c /infer/tests/build_systems/buck_flavors_diff/src/hello.c
/infer/tests/build_systems/codetoanalyze/ndk-build/hello_app/libs/
/infer/tests/build_systems/codetoanalyze/ndk-build/hello_app/obj/
/infer/tests/build_systems/codetoanalyze/utf8_*n_pwd
/infer/tests/build_systems/codetoanalyze/mvn/**/target/
/infer/tests/build_systems/codetoanalyze/path with spaces/
/infer/tests/build_systems/clang_compilation_db_escaped/compile_commands.json /infer/tests/build_systems/clang_compilation_db_escaped/compile_commands.json
/infer/tests/build_systems/clang_compilation_db_relpath/compile_commands.json /infer/tests/build_systems/clang_compilation_db_relpath/compile_commands.json
/infer/tests/build_systems/clang_test_determinator/*.test
/infer/tests/build_systems/clang_with_MD_flag/hello.d /infer/tests/build_systems/clang_with_MD_flag/hello.d
/infer/tests/build_systems/codetoanalyze/mvn/**/target/
/infer/tests/build_systems/codetoanalyze/ndk-build/hello_app/libs/
/infer/tests/build_systems/codetoanalyze/ndk-build/hello_app/obj/
/infer/tests/build_systems/codetoanalyze/path with spaces/
/infer/tests/build_systems/codetoanalyze/utf8_*n_pwd
/infer/tests/build_systems/codetoanalyze/xcodebuild/simple_app/app_built /infer/tests/build_systems/codetoanalyze/xcodebuild/simple_app/app_built
/infer/tests/build_systems/codetoanalyze/xcodebuild/simple_app/build/ /infer/tests/build_systems/codetoanalyze/xcodebuild/simple_app/build/
/infer/tests/build_systems/differential_*/**/*.class
/infer/tests/build_systems/differential_*/**/Diff*.java
/infer/tests/build_systems/diff/src /infer/tests/build_systems/diff/src
/infer/tests/build_systems/diff_*/src /infer/tests/build_systems/diff_*/src
/infer/tests/build_systems/buck_flavors_deterministic/capture_hash-*.sha /infer/tests/build_systems/differential_*/**/*.class
/infer/tests/build_systems/differential_*/**/Diff*.java
/infer/tests/build_systems/genrule/report.json /infer/tests/build_systems/genrule/report.json
/infer/tests/build_systems/java_test_determinator/*.test /infer/tests/build_systems/java_test_determinator/*.test
/infer/tests/codetoanalyze/java/classloads/*.loads /infer/tests/codetoanalyze/java/classloads/*.loads

@ -45,6 +45,7 @@ BUILD_SYSTEMS_TESTS += \
run_hidden_linters \ run_hidden_linters \
tracebugs \ tracebugs \
utf8_in_procname \ utf8_in_procname \
clang_test_determinator \
DIRECT_TESTS += \ DIRECT_TESTS += \
c_biabduction \ c_biabduction \

@ -1514,6 +1514,11 @@ INTERNAL OPTIONS
which speficy the relevant arguments. (Conversely: which speficy the relevant arguments. (Conversely:
--no-test-determinator) --no-test-determinator)
--test-determinator-clang
Activates: Run infer in Test Determinator mode for clang. It is
used together with the --modified-lines. (Conversely:
--no-test-determinator-clang)
--test-filtering --test-filtering
Activates: List all the files Infer can report on (should be Activates: List all the files Infer can report on (should be
called from the root of the project) (Conversely: called from the root of the project) (Conversely:

@ -1068,6 +1068,9 @@ let register_perf_stats_report () =
let main ~report_json = let main ~report_json =
if Config.test_determinator_clang then (
TestDeterminator.emit_tests_to_run () ;
TestDeterminator.emit_relevant_methods () ) ;
let issue_formats = init_issues_format_list report_json in let issue_formats = init_issues_format_list report_json in
let formats_by_report_kind = let formats_by_report_kind =
let costs_report_format_kind = let costs_report_format_kind =

@ -2163,6 +2163,12 @@ and test_determinator =
$(b,--test-profiler) flags, which speficy the relevant arguments." $(b,--test-profiler) flags, which speficy the relevant arguments."
and test_determinator_clang =
CLOpt.mk_bool ~long:"test-determinator-clang" ~default:false
"Run infer in Test Determinator mode for clang. It is used together with the \
$(b,--modified-lines)."
and test_filtering = and test_filtering =
CLOpt.mk_bool ~deprecated:["test_filtering"] ~long:"test-filtering" CLOpt.mk_bool ~deprecated:["test_filtering"] ~long:"test-filtering"
"List all the files Infer can report on (should be called from the root of the project)" "List all the files Infer can report on (should be called from the root of the project)"
@ -3024,6 +3030,8 @@ and keep_going = !keep_going
and test_determinator = !test_determinator and test_determinator = !test_determinator
and test_determinator_clang = !test_determinator_clang
and test_filtering = !test_filtering and test_filtering = !test_filtering
and profiler_samples = !profiler_samples and profiler_samples = !profiler_samples

@ -652,6 +652,8 @@ val symops_per_iteration : int option
val test_determinator : bool val test_determinator : bool
val test_determinator_clang : bool
val test_filtering : bool val test_filtering : bool
val testing_mode : bool val testing_mode : bool

@ -52,6 +52,8 @@ let do_source_file (translation_unit_context : CFrontend_config.translation_unit
let cfg = compute_icfg translation_unit_context tenv ast in let cfg = compute_icfg translation_unit_context tenv ast in
L.(debug Capture Verbose) L.(debug Capture Verbose)
"@\n End building call/cfg graph for '%a'.@\n" SourceFile.pp source_file ; "@\n End building call/cfg graph for '%a'.@\n" SourceFile.pp source_file ;
if Config.test_determinator_clang then
TestDeterminator.test_to_run_clang source_file cfg Config.modified_lines None ;
(* This part below is a boilerplate in every frontends. *) (* This part below is a boilerplate in every frontends. *)
(* This could be moved in the cfg_infer module *) (* This could be moved in the cfg_infer module *)
NullabilityPreanalysis.analysis cfg tenv ; NullabilityPreanalysis.analysis cfg tenv ;

@ -136,8 +136,8 @@ module DiffLines = struct
end end
let pp_profiler_sample_set fmt s = let pp_profiler_sample_set fmt s =
F.fprintf fmt " (size = %i) " (JPS.ProfilerSample.cardinal s) ; F.fprintf fmt " (set size = %i) " (JPS.ProfilerSample.cardinal s) ;
JPS.ProfilerSample.iter (fun m -> F.fprintf fmt "@\n > %a " Typ.Procname.pp m) s JPS.ProfilerSample.iter (fun m -> F.fprintf fmt "@\n <Method:> %a " Typ.Procname.pp m) s
module TestSample = struct module TestSample = struct
@ -201,7 +201,7 @@ let compute_affected_methods_clang source_file changed_lines_map method_range_ma
L.(debug TestDeterminator Medium) "found!@\n" ; L.(debug TestDeterminator Medium) "found!@\n" ;
let affected_methods = affected_methods method_range_map fname changed_lines in let affected_methods = affected_methods method_range_map fname changed_lines in
L.(debug TestDeterminator Medium) L.(debug TestDeterminator Medium)
"== Resulting Affected Methods ==@\n%a@\n== End Affected Methods ==@\n" "@\n@\n== Resulting Affected Methods ==%a@\n== End Affected Methods ==@\n\n"
pp_profiler_sample_set affected_methods ; pp_profiler_sample_set affected_methods ;
affected_methods affected_methods
| None -> | None ->
@ -212,6 +212,9 @@ let compute_affected_methods_clang source_file changed_lines_map method_range_ma
let relevant_tests = ref [] let relevant_tests = ref []
(* Methods modified in a diff *)
let relevant_methods = ref []
let _get_relevant_test_to_run () = !relevant_tests let _get_relevant_test_to_run () = !relevant_tests
let emit_tests_to_run () = let emit_tests_to_run () =
@ -221,14 +224,28 @@ let emit_tests_to_run () =
L.progress "Tests to run: [%a]@\n" (Pp.seq ~sep:", " F.pp_print_string) !relevant_tests L.progress "Tests to run: [%a]@\n" (Pp.seq ~sep:", " F.pp_print_string) !relevant_tests
let emit_relevant_methods () =
let methods = List.dedup_and_sort ~compare:String.compare !relevant_methods in
let json = `List (List.map ~f:(fun t -> `String t) methods) in
let outpath = Config.results_dir ^/ "diff_determinator.json" in
Yojson.Basic.to_file outpath json ;
L.progress "Methods modified in this Diff: [%a]@\n"
(Pp.seq ~sep:", " F.pp_print_string)
!relevant_methods
let init_clang cfg changed_lines_file test_samples_file = let init_clang cfg changed_lines_file test_samples_file =
DiffLines.init_changed_lines_map changed_lines_file ; DiffLines.init_changed_lines_map changed_lines_file ;
DiffLines.print_changed_lines () ; DiffLines.print_changed_lines () ;
MethodRangeMap.create_clang_method_range_map cfg ; MethodRangeMap.create_clang_method_range_map cfg ;
L.(debug TestDeterminator Medium) "%a@\n" MethodRangeMap.pp_map () ; L.(debug TestDeterminator Medium) "%a@\n" MethodRangeMap.pp_map () ;
TestSample.init_test_sample test_samples_file ; match test_samples_file with
L.(debug TestDeterminator Medium) "%a@\n" TestSample.pp_map () ; | Some _ ->
initialized_test_determinator := true TestSample.init_test_sample test_samples_file
| _ ->
() ;
L.(debug TestDeterminator Medium) "%a@\n" TestSample.pp_map () ;
initialized_test_determinator := true
let init_java changed_lines_file test_samples_file code_graph_file = let init_java changed_lines_file test_samples_file code_graph_file =
@ -242,7 +259,7 @@ let init_java changed_lines_file test_samples_file code_graph_file =
(* test_to_run = { n | Affected_Method /\ ts_n != 0 } *) (* test_to_run = { n | Affected_Method /\ ts_n != 0 } *)
let _test_to_run_clang source_file cfg changed_lines_file test_samples_file = let test_to_run_clang source_file cfg changed_lines_file test_samples_file =
L.(debug TestDeterminator Quiet) L.(debug TestDeterminator Quiet)
"****** Start Test Determinator for %s *****@\n" "****** Start Test Determinator for %s *****@\n"
(SourceFile.to_string source_file) ; (SourceFile.to_string source_file) ;
@ -251,6 +268,10 @@ let _test_to_run_clang source_file cfg changed_lines_file test_samples_file =
compute_affected_methods_clang source_file (DiffLines.changed_lines_map ()) compute_affected_methods_clang source_file (DiffLines.changed_lines_map ())
(MethodRangeMap.method_range_map ()) (MethodRangeMap.method_range_map ())
in in
let affected_methods_list =
JPS.ProfilerSample.fold (fun m acc -> Typ.Procname.to_string m :: acc) affected_methods []
in
relevant_methods := List.append affected_methods_list !relevant_methods ;
let test_to_run = let test_to_run =
if JPS.ProfilerSample.is_empty affected_methods then [] if JPS.ProfilerSample.is_empty affected_methods then []
else else

@ -9,9 +9,11 @@ open! IStd
val test_to_run_java : string option -> string option -> string option -> unit val test_to_run_java : string option -> string option -> string option -> unit
val _test_to_run_clang : val test_to_run_clang :
SourceFile.t -> Procdesc.t Typ.Procname.Hash.t -> string option -> string option -> unit SourceFile.t -> Procdesc.t Typ.Procname.Hash.t -> string option -> string option -> unit
val emit_tests_to_run : unit -> unit val emit_tests_to_run : unit -> unit
val emit_relevant_methods : unit -> unit
val _get_relevant_test_to_run : unit -> string list val _get_relevant_test_to_run : unit -> string list

@ -0,0 +1,46 @@
# Copyright (c) 2017-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# E2E test involving the test_determinator feature
TESTS_DIR = ../..
include $(TESTS_DIR)/base.make
A_CPP = A.cpp
TEST_DETERMINATOR_RESULT = infer-out-mod2/diff_determinator.json
DIFF_OUTPUT = diff.mod2.test
default: $(TEST_DETERMINATOR_RESULT)
$(DIFF_OUTPUT):
$(QUIET)echo -n '$(A_CPP):' > diff.mod1.test
$(QUIET)(diff -N --unchanged-line-format="U" --old-line-format="O" --new-line-format="N" \
orig-$(A_CPP) mod1-$(A_CPP) || [ $$? = 1 ]) >> diff.mod1.test
$(QUIET)echo -n '$(A_CPP):' > diff.mod2.test
$(QUIET)(diff -N --unchanged-line-format="U" --old-line-format="O" --new-line-format="N" \
orig-$(A_CPP) mod2-$(A_CPP) || [ $$? = 1 ]) >> diff.mod2.test
$(TEST_DETERMINATOR_RESULT): $(DIFF_OUTPUT)
$(QUIET)$(call silent_on_success,Testing test-determinator with set of changes in mod1,\
cp mod1-$(A_CPP) $(A_CPP);\
$(INFER_BIN) -o infer-out-mod1 --test-determinator-clang --modified-lines diff.mod1.test -- clang -c $(A_CPP))
$(QUIET)$(call silent_on_success,Testing test-determinator-clang with set of changes in mod2,\
cp mod2-$(A_CPP) $(A_CPP);\
$(INFER_BIN) -o infer-out-mod2 --test-determinator-clang --modified-lines diff.mod2.test -- clang -c $(A_CPP))
$(QUIET) rm $(A_CPP)
.PHONY: test
test: $(TEST_DETERMINATOR_RESULT)
$(QUIET)$(call check_no_diff,diff_determinator.json.mod1.exp,infer-out-mod1/diff_determinator.json)
$(QUIET)$(call check_no_diff,diff_determinator.json.mod2.exp,infer-out-mod2/diff_determinator.json)
.PHONY: replace
replace: $(TEST_DETERMINATOR_RESULT)
$(COPY) infer-out-mod1/diff_determinator.json diff_determinator.json.mod1.exp
$(COPY) infer-out-mod2/diff_determinator.json diff_determinator.json.mod2.exp
.PHONY: clean
clean:
$(REMOVE_DIR) *.test infer-out-mod* *.o

@ -0,0 +1 @@
["Shapes::Cube::area","Shapes::Cube::sort","Shapes::Cube::sort::lambda_A.cpp:24:25::","Shapes::Cube::sort::lambda_A.cpp:24:25::operator()"]

@ -0,0 +1,26 @@
/*
* Copyright (c) 2019-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <algorithm>
namespace Shapes {
class Cube {
int size;
public:
void set_size(int);
int area() { return size * size * size; };
void sort(Cube*, unsigned);
};
void Cube::set_size(int s) { size = s; }
void Cube::sort(Cube* xs, unsigned n) {
std::sort(xs, xs + n, [](Cube a, Cube b) { return (a.area() > b.area()); });
}
} // namespace Shapes

@ -0,0 +1,27 @@
/*
* Copyright (c) 2019-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <algorithm>
namespace Shapes {
class Cube {
int size;
public:
void set_size(int);
int area() { return size * size; };
void sort(Cube*, unsigned);
};
void Cube::set_size(int s) { size = s; }
void Cube::sort(Cube* xs, unsigned n) {
// this is a lambda folks
std::sort(xs, xs + n, [](Cube a, Cube b) { return (a.area() < b.area()); });
}
} // namespace Shapes

@ -0,0 +1,26 @@
/*
* Copyright (c) 2019-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <algorithm>
namespace Shapes {
class Cube {
int size;
public:
void set_size(int);
int area() { return size * size; };
void sort(Cube*, unsigned);
};
void Cube::set_size(int s) { size = s; }
void Cube::sort(Cube* xs, unsigned n) {
std::sort(xs, xs + n, [](Cube a, Cube b) { return (a.area() < b.area()); });
}
} // namespace Shapes
Loading…
Cancel
Save