diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index a08bcec00..bd7cf6788 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -28,7 +28,6 @@ struct then Format.fprintf else Format.ifprintf in pp Format.std_formatter fmt - let print_tenv tenv = Sil.tenv_iter (fun typname typ -> match typname with @@ -292,6 +291,11 @@ struct | _, _, _ -> false in append_no_duplicates field_eq list1 list2 + let sort_fields fields = + let compare (name1, _, _) (name2, _, _) = + Ident.fieldname_compare name1 name2 in + list_sort compare fields + let rec collect_list_tuples l (a, a1, b, c, d) = match l with | [] -> (a, a1, b, c, d) diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 7bb6a41b4..629910925 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -79,6 +79,8 @@ sig val append_no_duplicated_pvars : (Sil.exp * Sil.typ) list -> (Sil.exp * Sil.typ) list -> (Sil.exp * Sil.typ) list + val sort_fields : (Ident.fieldname * Sil.typ * Sil.item_annotation) list -> (Ident.fieldname * Sil.typ * Sil.item_annotation) list + val collect_list_tuples : ('a list * 'b list * 'c list * 'd list * 'e list) list -> 'a list * 'b list * 'c list * 'd list * 'e list -> 'a list * 'b list * 'c list * 'd list * 'e list diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 526850d7d..e5941272f 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -75,6 +75,7 @@ struct let item_annot = Sil.item_annotation_empty in fname, typ, item_annot in let fields = list_map mk_field_from_captured_var captured_vars in + let fields = CFrontend_utils.General_utils.sort_fields fields in Printing.log_out "Block %s field:\n" block_name; list_iter (fun (fn, ft, _) -> Printing.log_out "-----> field: '%s'\n" (Ident.fieldname_to_string fn)) fields; diff --git a/infer/src/clang/cTypes_decl.ml b/infer/src/clang/cTypes_decl.ml index 83f1b6b05..6141e612c 100644 --- a/infer/src/clang/cTypes_decl.ml +++ b/infer/src/clang/cTypes_decl.ml @@ -211,6 +211,7 @@ and get_declaration_type tenv namespace decl_info n opt_type decl_list decl_cont let non_static_fields = if CTrans_models.is_objc_memory_model_controlled n then append_no_duplicates_fields [Sil.objc_ref_counter_field] non_static_fields else non_static_fields in + let non_static_fields = CFrontend_utils.General_utils.sort_fields non_static_fields in let static_fields = [] in (* Warning for the moment we do not treat static field. *) let typ = (match opt_type with | `Type s -> qual_type_to_sil_type_no_expansions tenv (Ast_expressions.create_qual_type s) diff --git a/infer/src/clang/objcCategory_decl.ml b/infer/src/clang/objcCategory_decl.ml index c1c6c98e7..16955f1e6 100644 --- a/infer/src/clang/objcCategory_decl.ml +++ b/infer/src/clang/objcCategory_decl.ml @@ -48,6 +48,7 @@ let process_category tenv name class_name decl_list = match Sil.tenv_lookup tenv class_tn_name with | Some Sil.Tstruct (intf_fields, _, _, _, superclass, intf_methods, annotation) -> let new_fields = General_utils.append_no_duplicates_fields fields intf_fields in + let new_fields = CFrontend_utils.General_utils.sort_fields new_fields in let new_methods = General_utils.append_no_duplicates_methods methods intf_methods in let class_type_info = Sil.Tstruct ( diff --git a/infer/src/clang/objcInterface_decl.ml b/infer/src/clang/objcInterface_decl.ml index d31b40fc4..b129c4756 100644 --- a/infer/src/clang/objcInterface_decl.ml +++ b/infer/src/clang/objcInterface_decl.ml @@ -111,6 +111,7 @@ let add_class_to_tenv tenv class_name decl_list obj_c_interface_decl_info = let fields = append_no_duplicates_fields fields fields_sc in (* We add the special hidden counter_field for implementing reference counting *) let fields = append_no_duplicates_fields [Sil.objc_ref_counter_field] fields in + let fields = CFrontend_utils.General_utils.sort_fields fields in Printing.log_out "Class %s field:\n" class_name; list_iter (fun (fn, ft, _) -> Printing.log_out "-----> field: '%s'\n" (Ident.fieldname_to_string fn)) fields; @@ -152,6 +153,7 @@ let add_missing_fields tenv class_name decl_list idi = " ---> Extra non-static field: '%s'\n" (Ident.fieldname_to_string fn)) extra_fields; let new_fields = append_no_duplicates_fields extra_fields intf_fields in + let new_fields = CFrontend_utils.General_utils.sort_fields new_fields in let class_type_info = Sil.Tstruct ( new_fields, [], Sil.Class, Some mang_name, superclass, methods, annotation diff --git a/infer/tests/codetoanalyze/c/errors/Makefile b/infer/tests/codetoanalyze/c/errors/Makefile index 1d512580e..5b93dd82d 100644 --- a/infer/tests/codetoanalyze/c/errors/Makefile +++ b/infer/tests/codetoanalyze/c/errors/Makefile @@ -7,6 +7,7 @@ all: make -C null_dereference make -C resource_leaks make -C memory_leaks + make -C lists clean: make -C arithmetic clean @@ -16,4 +17,5 @@ clean: make -C null_dereference clean make -C resource_leaks clean make -C memory_leaks + make -C lists diff --git a/infer/tests/codetoanalyze/c/errors/lists/Makefile b/infer/tests/codetoanalyze/c/errors/lists/Makefile new file mode 100644 index 000000000..ec67ebe4f --- /dev/null +++ b/infer/tests/codetoanalyze/c/errors/lists/Makefile @@ -0,0 +1,12 @@ + +SOURCES = $(shell ls *.c) +OBJECTS = $(SOURCES:.c=.o) + +all: clean $(OBJECTS) + echo $(OBJECTS) + +.c.o: + ${CC} -c $< + +clean: + rm -rf $(OBJECTS) diff --git a/infer/tests/codetoanalyze/c/errors/lists/lists.c b/infer/tests/codetoanalyze/c/errors/lists/lists.c new file mode 100644 index 000000000..4954fdf32 --- /dev/null +++ b/infer/tests/codetoanalyze/c/errors/lists/lists.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015 - Facebook. + * All rights reserved. + */ +struct l2 { + int b; + struct l2 *a; +}; + +int add2(struct l2 *l) { + int r = 0; + for (; l; l = l->a) { + r += l->b; + } + return r; +} + +/* Divide by zero error shows that we get a spec for add2 */ +int main() { + int res = add2(0); + return 5/res; +} diff --git a/infer/tests/codetoanalyze/c/frontend/initialization/struct_initlistexpr.dot b/infer/tests/codetoanalyze/c/frontend/initialization/struct_initlistexpr.dot index 95b1b0928..f73f06462 100644 --- a/infer/tests/codetoanalyze/c/frontend/initialization/struct_initlistexpr.dot +++ b/infer/tests/codetoanalyze/c/frontend/initialization/struct_initlistexpr.dot @@ -1,5 +1,5 @@ digraph iCFG { -14 [label="14: InitListExp \n *&e.ssn:int =12 [line 37]\n *&e.salary:float =3000.500000 [line 37]\n *&e.doj.date:int =12 [line 37]\n *&e.doj.month:int =12 [line 37]\n *&e.doj.year:int =2010 [line 37]\n " shape="box"] +14 [label="14: InitListExp \n *&e.doj.date:int =12 [line 37]\n *&e.doj.month:int =3000.500000 [line 37]\n *&e.doj.year:int =12 [line 37]\n *&e.salary:float =12 [line 37]\n *&e.ssn:int =2010 [line 37]\n " shape="box"] 14 -> 13 ; diff --git a/infer/tests/codetoanalyze/objc/frontend/block/block.dot b/infer/tests/codetoanalyze/objc/frontend/block/block.dot index 55bad466b..0822d44d6 100644 --- a/infer/tests/codetoanalyze/objc/frontend/block/block.dot +++ b/infer/tests/codetoanalyze/objc/frontend/block/block.dot @@ -26,7 +26,7 @@ digraph iCFG { 19 -> 18 ; -18 [label="18: BinaryOperatorStmt: Assign \n DECLARE_LOCALS(&__objc_anonymous_block___objc_anonymous_block_main1______2______3); [line 21]\n n$22=_fun___objc_alloc_no_fail(sizeof(class __objc_anonymous_block___objc_anonymous_block_main1______2______3 ):class __objc_anonymous_block___objc_anonymous_block_main1______2______3 *) [line 21]\n *&__objc_anonymous_block___objc_anonymous_block_main1______2______3:class __objc_anonymous_block___objc_anonymous_block_main1______2______3 =n$22 [line 21]\n n$23=*&x:int [line 21]\n n$24=*&bla:int [line 21]\n *n$22.x:int =n$23 [line 21]\n *n$22.bla:int =n$24 [line 21]\n n$16=*&x:int [line 21]\n n$17=*&bla:int [line 21]\n *&addblock2:_fn_ (*)=(_fun___objc_anonymous_block___objc_anonymous_block_main1______2______3,n$16,n$17) [line 21]\n REMOVE_TEMPS(n$22,n$23,n$24,n$16,n$17); [line 21]\n " shape="box"] +18 [label="18: BinaryOperatorStmt: Assign \n DECLARE_LOCALS(&__objc_anonymous_block___objc_anonymous_block_main1______2______3); [line 21]\n n$22=_fun___objc_alloc_no_fail(sizeof(class __objc_anonymous_block___objc_anonymous_block_main1______2______3 ):class __objc_anonymous_block___objc_anonymous_block_main1______2______3 *) [line 21]\n *&__objc_anonymous_block___objc_anonymous_block_main1______2______3:class __objc_anonymous_block___objc_anonymous_block_main1______2______3 =n$22 [line 21]\n n$23=*&x:int [line 21]\n n$24=*&bla:int [line 21]\n *n$22.bla:int =n$23 [line 21]\n *n$22.x:int =n$24 [line 21]\n n$16=*&x:int [line 21]\n n$17=*&bla:int [line 21]\n *&addblock2:_fn_ (*)=(_fun___objc_anonymous_block___objc_anonymous_block_main1______2______3,n$16,n$17) [line 21]\n REMOVE_TEMPS(n$22,n$23,n$24,n$16,n$17); [line 21]\n " shape="box"] 18 -> 14 ; diff --git a/infer/tests/endtoend/c/ListsTest.java b/infer/tests/endtoend/c/ListsTest.java new file mode 100644 index 000000000..aea366149 --- /dev/null +++ b/infer/tests/endtoend/c/ListsTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013- Facebook. + * All rights reserved. + */ + +package endtoend.c; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; + +import utils.InferException; +import utils.InferResults; + +public class ListsTest { + + public static final String SOURCE_FILE = "lists/lists.c"; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + + private static InferResults inferResults; + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferResults = InferResults.loadCInferResults(DivideByZeroTest.class, SOURCE_FILE); + } + + @Test + public void whenInferRunsOnDivideByZeroThenDivideByZeroIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = {"main"}; + assertThat( + "Results should contain divide by zero error", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + SOURCE_FILE, + procedures + ) + ); + } + + +}