[proc-cfg][2/5] fix duplicate symbols detection

Summary:
Fix the logic for computing duplicate symbols. It was broken at some point and some duplicate symbols creeped into our tests. Fix these, and add a test to avoid duplicate symbols detection to regress again.

Also, this removes one use of `Cfg.load`, on the way to removing file-wide CFGs from the database.

Reviewed By: ngorogiannis

Differential Revision: D10173349

fbshipit-source-id: a0d2365b3
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 7e20c8d380
commit 7615963bf4

@ -35,6 +35,7 @@ BUILD_SYSTEMS_TESTS += \
delete_results_dir \ delete_results_dir \
diff \ diff \
diff_gen_build_script \ diff_gen_build_script \
duplicate_symbols \
fail_on_issue \ fail_on_issue \
j1 \ j1 \
linters \ linters \

@ -190,6 +190,8 @@ let pp_all ~filter ~cfgs ~type_environment ~procedure_names ~freshly_captured fm
in in
let pp_result fmt stmt = let pp_result fmt stmt =
Container.iter stmt ~f:(pp fmt) Container.iter stmt ~f:(pp fmt)
~fold:(SqliteUtils.result_fold_single_column_rows db ~log:"printing all source files") ~fold:
(SqliteUtils.result_fold_single_column_rows ~finalize:false db
~log:"printing all source files")
in in
F.fprintf fmt "@[<v>%a@]" pp_result stmt ) F.fprintf fmt "@[<v>%a@]" pp_result stmt )

@ -98,20 +98,22 @@ let iterate_cluster_callbacks all_procs exe_env source_file =
!cluster_callbacks !cluster_callbacks
let dump_duplicate_procs (exe_env : Exe_env.t) source_file procs = let dump_duplicate_procs source_file procs =
let duplicate_procs = let duplicate_procs =
List.filter_map procs ~f:(fun pname -> List.filter_map procs ~f:(fun pname ->
match Exe_env.get_proc_desc exe_env pname with match Attributes.load pname with
| Some pdesc when (* defined in the current file *) Procdesc.is_defined pdesc -> ( | Some
match Attributes.load pname with { is_defined=
| Some {translation_unit; loc} true
when (* defined in another file *) (* likely not needed: if [pname] is part of [procs] then it *is* defined, so we
(not (SourceFile.equal source_file translation_unit)) expect the attribute to be defined too *)
&& (* really defined in the current file and not in an include *) ; translation_unit
SourceFile.equal source_file loc.file -> ; loc }
Some (pname, translation_unit) when (* defined in another file *)
| _ -> (not (SourceFile.equal source_file translation_unit))
None ) && (* really defined in that file and not in an include *)
SourceFile.equal translation_unit loc.file ->
Some (pname, translation_unit)
| _ -> | _ ->
None ) None )
in in
@ -120,8 +122,9 @@ let dump_duplicate_procs (exe_env : Exe_env.t) source_file procs =
~append:true ~perm:0o666 ~f:(fun outc -> ~append:true ~perm:0o666 ~f:(fun outc ->
let fmt = F.formatter_of_out_channel outc in let fmt = F.formatter_of_out_channel outc in
List.iter duplicate_procs ~f:(fun (pname, source_captured) -> List.iter duplicate_procs ~f:(fun (pname, source_captured) ->
F.fprintf fmt "@.DUPLICATE_SYMBOLS source:%a source_captured:%a pname:%a@." F.fprintf fmt "DUPLICATE_SYMBOLS source:%a source_captured:%a pname:%a@\n"
SourceFile.pp source_file SourceFile.pp source_captured Typ.Procname.pp pname ) ) SourceFile.pp source_file SourceFile.pp source_captured Typ.Procname.pp pname ) ;
F.pp_print_flush fmt () )
in in
if not (List.is_empty duplicate_procs) then output_to_file duplicate_procs if not (List.is_empty duplicate_procs) then output_to_file duplicate_procs
@ -141,7 +144,7 @@ let analyze_file (exe_env : Exe_env.t) source_file =
(* analyze all the currently defined procedures *) (* analyze all the currently defined procedures *)
SourceFiles.proc_names_of_source source_file SourceFiles.proc_names_of_source source_file
in in
if Config.dump_duplicate_symbols then dump_duplicate_procs exe_env source_file procs_to_analyze ; if Config.dump_duplicate_symbols then dump_duplicate_procs source_file procs_to_analyze ;
let analyze_proc_name pname = ignore (Ondemand.analyze_proc_name pname : Summary.t option) in let analyze_proc_name pname = ignore (Ondemand.analyze_proc_name pname : Summary.t option) in
List.iter ~f:analyze_proc_name procs_to_analyze ; List.iter ~f:analyze_proc_name procs_to_analyze ;
(* Invoke cluster callbacks. *) (* Invoke cluster callbacks. *)

@ -13,8 +13,13 @@ TEST_REL_DIR = $(patsubst $(abspath $(TESTS_DIR))/%,%,$(abspath $(CURDIR)))
define check_no_duplicates define check_no_duplicates
if grep -q "DUPLICATE_SYMBOLS" $(1); then \ if grep -q "DUPLICATE_SYMBOLS" $(1); then \
printf '$(TERM_ERROR)Duplicate symbols found in $(CURDIR).$(TERM_RESET)\n' >&2; \ printf '$(TERM_ERROR)Duplicate symbols found in $(CURDIR):$(TERM_RESET)\n' >&2; \
printf '$(TERM_ERROR)Please make sure all the function names in all the source test files are different.$(TERM_RESET)' >&2; \ printf '$(TERM_ERROR)========$(TERM_RESET)\n' >&2; \
while read line; do \
printf '$(TERM_ERROR)%s$(TERM_RESET)\n' "$$line" >&2; \
done <$(1); \
printf '$(TERM_ERROR)========$(TERM_RESET)\n' >&2; \
printf '$(TERM_ERROR)Please make sure all the function names in all the source test files are different.$(TERM_RESET)\n' >&2; \
exit 1; \ exit 1; \
fi fi
endef endef

@ -0,0 +1,13 @@
/*
* Copyright (c) 2015-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 <stdlib.h>
void test() {
int* s = NULL;
*s = 42;
}

@ -0,0 +1,19 @@
# Copyright (c) 2016-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.
TESTS_DIR = ../..
CLANG_OPTIONS = -c
INFER_OPTIONS = --project-root ../codetoanalyze
INFERPRINT_OPTIONS = --issues-tests
SOURCES = ../codetoanalyze/hello.c ../codetoanalyze/hello_dup.c
include $(TESTS_DIR)/clang.make
issues.exp.test:
$(QUIET)cat infer-out$(TEST_SUFFIX)/duplicates.txt > $@
# delete duplicates file to avoid throwing an error because duplicate symbols were found
$(QUIET)$(REMOVE) infer-out$(TEST_SUFFIX)/duplicates.txt

@ -0,0 +1 @@
DUPLICATE_SYMBOLS source:hello.c source_captured:hello_dup.c pname:test

@ -17,4 +17,3 @@ infer-out$(TEST_SUFFIX)/report.json: $(CLANG_DEPS) $(SOURCES) $(HEADERS) $(TESTS
$(INFER_BIN) --results-dir $(@D) --dump-duplicate-symbols \ $(INFER_BIN) --results-dir $(@D) --dump-duplicate-symbols \
$(INFER_OPTIONS) -- \ $(INFER_OPTIONS) -- \
clang $(CLANG_OPTIONS) $(SOURCES)) clang $(CLANG_OPTIONS) $(SOURCES))
$(QUIET)$(call check_no_duplicates,infer-out$(TEST_SUFFIX)/duplicates.txt)

@ -37,14 +37,14 @@ void for_loop() {
} }
} }
void nop() { int k = 0; } void no_op() { int k = 0; }
int two_loops(int m) { int two_loops(int m) {
for (int i = 0; i < m; i++) { for (int i = 0; i < m; i++) {
nop(); no_op();
} }
for (int j = 0; j < m; j++) { for (int j = 0; j < m; j++) {
nop(); no_op();
} }
return m; return m;
} }

@ -5,12 +5,12 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
void nop() { int x = 0; } void no_op() { int x = 0; }
// Expected: Theta(n) // Expected: Theta(n)
void do_n_times(int n) { void do_n_times(int n) {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
nop(); no_op();
} }
} }

@ -9,22 +9,22 @@ struct B {
int f; int f;
}; };
struct A { struct WrapsB {
A(int f) { b = new B(f); } WrapsB(int f) { b = new B(f); }
B* b; B* b;
B* getb() { return b; }; B* getb() { return b; };
~A() { delete b; } ~WrapsB() { delete b; }
}; };
struct ReferenceWrapperHeap { struct ReferenceWrapperHeap {
ReferenceWrapperHeap(A& a) : b(a.getb()){}; ReferenceWrapperHeap(WrapsB& a) : b(a.getb()){};
B* b; B* b;
}; };
ReferenceWrapperHeap getwrapperHeap() { ReferenceWrapperHeap getwrapperHeap() {
A a(1); WrapsB a(1);
return a; // We store a.b in ReferenceWrapper, but we delete a.b in the return a; // We store a.b in ReferenceWrapper, but we delete a.b in the
// destructor of A // destructor of WrapsB
} }
int FN_reference_wrapper_heap_bad() { int FN_reference_wrapper_heap_bad() {

@ -9,50 +9,50 @@
#include <string> #include <string>
#include <vector> #include <vector>
struct S { struct Simple {
int f; int f;
}; };
void deref_deleted_bad() { void deref_deleted_bad() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
S tmp = *s; Simple tmp = *s;
} }
S* return_deleted_bad() { Simple* return_deleted_bad() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
return s; return s;
} }
S* reassign_deleted_ok() { Simple* reassign_deleted_ok() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
s = new S{2}; s = new Simple{2};
return s; return s;
} }
void reassign_field_of_deleted_bad() { void reassign_field_of_deleted_bad() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
s->f = 7; s->f = 7;
} }
void reassign_field_of_reinitialized_ok(S* tmp) { void reassign_field_of_reinitialized_ok(Simple* tmp) {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
s = tmp; s = tmp;
s->f = 7; s->f = 7;
} }
void double_delete_bad() { void double_delete_bad() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
delete s; delete s;
} }
S* delete_in_branch_bad(bool b) { Simple* delete_in_branch_bad(bool b) {
auto s = new S{1}; auto s = new Simple{1};
if (b) { if (b) {
delete s; delete s;
} }
@ -60,7 +60,7 @@ S* delete_in_branch_bad(bool b) {
} }
void delete_in_branch_ok(bool b) { void delete_in_branch_ok(bool b) {
auto s = new S{1}; auto s = new Simple{1};
if (b) { if (b) {
delete s; delete s;
} else { } else {
@ -69,7 +69,7 @@ void delete_in_branch_ok(bool b) {
} }
void use_in_branch_bad(bool b) { void use_in_branch_bad(bool b) {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
if (b) { if (b) {
auto tmp = *s; auto tmp = *s;
@ -77,7 +77,7 @@ void use_in_branch_bad(bool b) {
} }
void delete_in_loop_bad() { void delete_in_loop_bad() {
auto s = new S{1}; auto s = new Simple{1};
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
delete s; delete s;
} }
@ -85,7 +85,7 @@ void delete_in_loop_bad() {
void delete_in_loop_ok() { void delete_in_loop_ok() {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
} }
} }
@ -99,15 +99,15 @@ void delete_ref_in_loop_ok(int j, std::vector<std::string> v) {
} }
void use_in_loop_bad() { void use_in_loop_bad() {
auto s = new S{1}; auto s = new Simple{1};
delete s; delete s;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
s->f = i; s->f = i;
} }
} }
S* gated_delete_abort_ok(bool b) { Simple* gated_delete_abort_ok(bool b) {
auto s = new S{1}; auto s = new Simple{1};
if (b) { if (b) {
delete s; delete s;
std::abort(); std::abort();
@ -115,8 +115,8 @@ S* gated_delete_abort_ok(bool b) {
return s; return s;
} }
S* gated_exit_abort_ok(bool b) { Simple* gated_exit_abort_ok(bool b) {
auto s = new S{1}; auto s = new Simple{1};
if (b) { if (b) {
delete s; delete s;
exit(1); exit(1);
@ -124,8 +124,8 @@ S* gated_exit_abort_ok(bool b) {
return s; return s;
} }
S* gated_delete_throw_ok(bool b) { Simple* gated_delete_throw_ok(bool b) {
auto s = new S{1}; auto s = new Simple{1};
if (b) { if (b) {
delete s; delete s;
throw 5; throw 5;

@ -6,7 +6,7 @@
*/ */
#include <string> #include <string>
class A { class SomeStructA {
public: public:
int* ptr; int* ptr;
@ -14,11 +14,11 @@ class A {
std::string* p_a_name; std::string* p_a_name;
int i; int i;
A() {} // user defined default constructor. SomeStructA() {} // user defined default constructor.
// Does not initialize i so, it gets random value // Does not initialize i so, it gets random value
}; };
class B { class SomeStructB {
public: public:
std::string a_name; std::string a_name;
@ -29,7 +29,7 @@ class B {
}; };
int access_members_bad() { int access_members_bad() {
A a; SomeStructA a;
std::string n = a.a_name; std::string n = a.a_name;
int i = a.i; // error int i = a.i; // error
@ -38,7 +38,7 @@ int access_members_bad() {
}; };
int access_members_bad2() { int access_members_bad2() {
A a{}; SomeStructA a{};
std::string n = a.a_name; std::string n = a.a_name;
int i = a.i; // error int i = a.i; // error
@ -48,7 +48,7 @@ int access_members_bad2() {
int access_members_ok() { int access_members_ok() {
B b{}; // call default implicit constructor which initialize i. SomeStructB b{}; // call default implicit constructor which initialize i.
std::string n = b.a_name; std::string n = b.a_name;
int i = b.i; int i = b.i;
@ -57,7 +57,7 @@ int access_members_ok() {
int access_members_bad3() { int access_members_bad3() {
B b; // no constructor is called SomeStructB b; // no constructor is called
std::string n = b.a_name; std::string n = b.a_name;
int i = b.i; int i = b.i;
@ -65,7 +65,7 @@ int access_members_bad3() {
}; };
int access_pointer_members_bad() { int access_pointer_members_bad() {
A a; SomeStructA a;
int* p = a.ptr; int* p = a.ptr;
int i = a.i; int i = a.i;

@ -126,19 +126,16 @@ infer-out-all/report.json: $(CLANG_DEPS) $(SOURCES_BUCKET_ALL)
$(QUIET)$(call silent_on_success,Testing infer/Objective-C with all memleak buckets,\ $(QUIET)$(call silent_on_success,Testing infer/Objective-C with all memleak buckets,\
$(INFER_BIN) $(INFER_OPTIONS) --ml-buckets all -o infer-out-all -- \ $(INFER_BIN) $(INFER_OPTIONS) --ml-buckets all -o infer-out-all -- \
clang $(CLANG_OPTIONS) $(SOURCES_BUCKET_ALL)) clang $(CLANG_OPTIONS) $(SOURCES_BUCKET_ALL))
$(QUIET)$(call check_no_duplicates,infer-out-all/duplicates.txt)
infer-out-arc/report.json: $(CLANG_DEPS) $(SOURCES_ARC) infer-out-arc/report.json: $(CLANG_DEPS) $(SOURCES_ARC)
$(QUIET)$(call silent_on_success,Testing infer/Objective-C with arc memleak buckets,\ $(QUIET)$(call silent_on_success,Testing infer/Objective-C with arc memleak buckets,\
$(INFER_BIN) $(INFER_OPTIONS) --ml-buckets cf -o infer-out-arc -- \ $(INFER_BIN) $(INFER_OPTIONS) --ml-buckets cf -o infer-out-arc -- \
clang $(CLANG_OPTIONS) -fobjc-arc $(SOURCES_ARC)) clang $(CLANG_OPTIONS) -fobjc-arc $(SOURCES_ARC))
$(QUIET)$(call check_no_duplicates,infer-out-arc/duplicates.txt)
infer-out/report.json: $(CLANG_DEPS) $(SOURCES_DEFAULT) infer-out/report.json: $(CLANG_DEPS) $(SOURCES_DEFAULT)
$(QUIET)$(call silent_on_success,Testing infer/Objective-C with CF memleak buckets,\ $(QUIET)$(call silent_on_success,Testing infer/Objective-C with CF memleak buckets,\
$(INFER_BIN) $(INFER_OPTIONS) --ml-buckets cf -o infer-out -- \ $(INFER_BIN) $(INFER_OPTIONS) --ml-buckets cf -o infer-out -- \
clang $(CLANG_OPTIONS) $(SOURCES_DEFAULT)) clang $(CLANG_OPTIONS) $(SOURCES_DEFAULT))
$(QUIET)$(call check_no_duplicates,infer-out/duplicates.txt)
issues.exp.test: infer-out-all/report.json infer-out-arc/report.json infer-out/report.json issues.exp.test: infer-out-all/report.json infer-out-arc/report.json infer-out/report.json
$(QUIET)$(INFER_BIN) report -q $(INFERPRINT_OPTIONS) $@.all \ $(QUIET)$(INFER_BIN) report -q $(INFERPRINT_OPTIONS) $@.all \
@ -148,3 +145,4 @@ issues.exp.test: infer-out-all/report.json infer-out-arc/report.json infer-out/r
$(QUIET)$(INFER_BIN) report -q $(INFERPRINT_OPTIONS) $@.default \ $(QUIET)$(INFER_BIN) report -q $(INFERPRINT_OPTIONS) $@.default \
-o infer-out --from-json-report infer-out/report.json -o infer-out --from-json-report infer-out/report.json
$(QUIET)cat $@.all $@.arc $@.default > $@ $(QUIET)cat $@.all $@.arc $@.default > $@
$(QUIET)cat infer-out-all/duplicates.txt infer-out-arc/duplicates.txt >> infer-out/duplicates.txt

@ -10,9 +10,9 @@ typedef struct Point {
int y; int y;
} Point; } Point;
int foo() { return 5; } int return_5() { return 5; }
int main() { struct Point p = {1, foo() + 3}; } void init_Point() { struct Point p = {1, return_5() + 3}; }
int point_coords_set_correctly(Point* p) { int point_coords_set_correctly(Point* p) {
*p = (Point){4, 5}; *p = (Point){4, 5};

@ -26,6 +26,7 @@ print: issues.exp.test$(TEST_SUFFIX)
test: issues.exp.test$(TEST_SUFFIX) test: issues.exp.test$(TEST_SUFFIX)
$(QUIET)cd $(TESTS_DIR) && \ $(QUIET)cd $(TESTS_DIR) && \
$(call check_no_diff,$(TEST_REL_DIR)/issues.exp$(TEST_RESULT_SUFFIX),$(TEST_REL_DIR)/issues.exp.test$(TEST_SUFFIX)) $(call check_no_diff,$(TEST_REL_DIR)/issues.exp$(TEST_RESULT_SUFFIX),$(TEST_REL_DIR)/issues.exp.test$(TEST_SUFFIX))
$(QUIET)$(call check_no_duplicates,$(INFER_OUT)/duplicates.txt)
.PHONY: print .PHONY: print
replace: issues.exp.test$(TEST_SUFFIX) replace: issues.exp.test$(TEST_SUFFIX)

Loading…
Cancel
Save