diff --git a/infer/src/IR/sil.ml b/infer/src/IR/sil.ml index e7d59a1cf..db963ea54 100644 --- a/infer/src/IR/sil.ml +++ b/infer/src/IR/sil.ml @@ -900,6 +900,12 @@ let is_cpp_class typ = let is_java_class typ = is_class_of_kind typ Csu.Java +let rec is_array_of_cpp_class typ = + match typ with + | Tarray (typ, _) -> + is_array_of_cpp_class typ + | _ -> is_cpp_class typ + (** turn a *T into a T. fails if [typ] is not a pointer type *) let typ_strip_ptr = function | Tptr (t, _) -> t @@ -1438,6 +1444,12 @@ let typ_equal t1 t2 = let exp_equal e1 e2 = exp_compare e1 e2 = 0 +let rec exp_is_array_index_of exp1 exp2 = + match exp1 with + | Lindex (exp, _) -> + exp_is_array_index_of exp exp2 + | _ -> exp_equal exp1 exp2 + let ident_exp_compare = pair_compare Ident.compare exp_compare diff --git a/infer/src/IR/sil.mli b/infer/src/IR/sil.mli index ebcb83dba..e22b564b6 100644 --- a/infer/src/IR/sil.mli +++ b/infer/src/IR/sil.mli @@ -561,6 +561,8 @@ val is_cpp_class : typ -> bool val is_java_class : typ -> bool +val is_array_of_cpp_class : typ -> bool + val exp_is_zero : exp -> bool val exp_is_null_literal : exp -> bool @@ -667,6 +669,9 @@ val exp_compare : exp -> exp -> int val exp_equal : exp -> exp -> bool +(** exp_is_array_index_of index arr returns true is index is an array index of arr. *) +val exp_is_array_index_of : exp -> exp -> bool + val call_flags_compare : call_flags -> call_flags -> int val exp_typ_compare : (exp * typ) -> (exp * typ) -> int diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 4d1243bec..56fa8ebce 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -200,6 +200,9 @@ struct let collect_exprs res_trans_list = IList.flatten (IList.map (fun res_trans -> res_trans.exps) res_trans_list) + let collect_initid_exprs res_trans_list = + IList.flatten (IList.map (fun res_trans -> res_trans.initd_exps) res_trans_list) + (* If e is a block and the calling node has the priority then *) (* we need to release the priority to allow*) (* creation of nodes inside the block.*) @@ -1590,6 +1593,26 @@ struct let loop = Clang_ast_t.WhileStmt (stmt_info, [null_stmt; cond; body']) in instruction trans_state (Clang_ast_t.CompoundStmt (stmt_info, [assign_next_object; loop])) + and initListExpr_initializers_trans trans_state var_exp n stmts typ = + let (var_exp_inside, typ_inside) = match typ with + | Sil.Tarray (t, _) when Sil.is_array_of_cpp_class typ -> + Sil.Lindex (var_exp, Sil.Const (Sil.Cint (Sil.Int.of_int n))), t + | _ -> var_exp, typ in + let trans_state' = { trans_state with var_exp_typ = Some (var_exp_inside, typ_inside) } in + match stmts with + | [] -> [] + | stmt :: rest -> + let rest_stmts_res_trans = + initListExpr_initializers_trans trans_state var_exp (n + 1) rest typ in + match stmt with + | Clang_ast_t.InitListExpr (_ , stmts , _) -> + let inside_stmts_res_trans = + initListExpr_initializers_trans trans_state var_exp_inside 0 stmts typ_inside in + inside_stmts_res_trans @ rest_stmts_res_trans + | _ -> + let stmt_res_trans = instruction trans_state' stmt in + stmt_res_trans :: rest_stmts_res_trans + and initListExpr_trans trans_state stmt_info expr_info stmts = let context = trans_state.context in let tenv = context.tenv in @@ -1604,11 +1627,7 @@ struct let var_type = CTypes_decl.type_ptr_to_sil_type context.CContext.tenv expr_info.Clang_ast_t.ei_type_ptr in let lh = var_or_zero_in_init_list tenv var_exp var_type ~return_zero:false in - let rec collect_right_hand_exprs stmt = match stmt with - | Clang_ast_t.InitListExpr (_ , stmts , _) -> - CTrans_utils.collect_res_trans (IList.map collect_right_hand_exprs stmts) - | _ -> instruction trans_state stmt in - let res_trans_subexpr_list = IList.map collect_right_hand_exprs stmts in + let res_trans_subexpr_list = initListExpr_initializers_trans trans_state var_exp 0 stmts typ in let rh_exps = collect_exprs res_trans_subexpr_list in if IList.length rh_exps == 0 then let exp = Sil.zero_value_of_numerical_type var_type in @@ -1624,7 +1643,17 @@ struct if IList.length rh_exps == IList.length lh then (* Creating new instructions by assigning right hand side to left hand side expressions *) let assign_instr (lh_exp, lh_t) (rh_exp, _) = Sil.Set (lh_exp, lh_t, rh_exp, sil_loc) in - let assign_instrs = IList.map2 assign_instr lh rh_exps in + let assign_instrs = + let initd_exps = collect_initid_exprs res_trans_subexpr_list in + (* If the variable var_exp is of type array, and some of its indices were initialized *) + (* by some constructor call, which we can tell by the fact that the index is returned *) + (* in initd_exps, then we assume that all the indices were initialized and *) + (* we don't need any assignments. *) + if IList.exists + ((fun arr index -> Sil.exp_is_array_index_of index arr) var_exp) + initd_exps + then [] + else IList.map2 assign_instr lh rh_exps in let initlist_expr_res = { empty_res_trans with exps = [(var_exp, var_type)]; diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp new file mode 100644 index 000000000..ccd458182 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 - 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. + */ + +class Person { + public: + Person(int i) { x = i; } + Person() {} + int x; +}; + +int array_of_person() { + Person arr[10] = {Person(), Person(), Person()}; + return (arr[0]).x; +} + +int matrix_of_person() { + Person arr[2][2] = {Person(), Person(), Person(), Person()}; + return (arr[0][1]).x; +} + +struct Z { + int a; + int b; +}; + +void initialization_c_style() { + struct Z z[2] = {{1, 2}, {2, 3}}; + struct Z z2; +} + +// Our handling assumes that either all the array elements are initialised +// with a constructor or not, so this doesn't work. +void initialization_mixed_styles_not_handled_correctly() { + struct Z old; + struct Z z[2] = {{1, 2}, old}; + struct Z z2; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp.dot new file mode 100644 index 000000000..b09571540 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_array.cpp.dot @@ -0,0 +1,117 @@ +digraph iCFG { +31 [label="31: DeclStmt \n _fun_Z_Z(&old:class Z *) [line 40]\n " shape="box"] + + + 31 -> 30 ; +30 [label="30: DeclStmt \n _fun_Z_Z(&z[1]:class Z *,&old:class Z &) [line 41]\n " shape="box"] + + + 30 -> 29 ; +29 [label="29: DeclStmt \n _fun_Z_Z(&z2:class Z *) [line 42]\n NULLIFY(&old,false); [line 42]\n NULLIFY(&z,false); [line 42]\n NULLIFY(&z2,false); [line 42]\n APPLY_ABSTRACTION; [line 42]\n " shape="box"] + + + 29 -> 28 ; +28 [label="28: Exit initialization_mixed_styles_not_handled_correctly \n " color=yellow style=filled] + + +27 [label="27: Start initialization_mixed_styles_not_handled_correctly\nFormals: \nLocals: z2:class Z z:class Z [2] old:class Z \n DECLARE_LOCALS(&return,&z2,&z,&old); [line 39]\n " color=yellow style=filled] + + + 27 -> 31 ; +26 [label="26: DeclStmt \n *&z[0].a:int =1 [line 33]\n *&z[0].b:int =2 [line 33]\n *&z[1].a:int =2 [line 33]\n *&z[1].b:int =3 [line 33]\n " shape="box"] + + + 26 -> 25 ; +25 [label="25: DeclStmt \n _fun_Z_Z(&z2:class Z *) [line 34]\n NULLIFY(&z2,false); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] + + + 25 -> 24 ; +24 [label="24: Exit initialization_c_style \n " color=yellow style=filled] + + +23 [label="23: Start initialization_c_style\nFormals: \nLocals: z2:class Z z:class Z [2] \n DECLARE_LOCALS(&return,&z2,&z); [line 32]\n NULLIFY(&z,false); [line 32]\n " color=yellow style=filled] + + + 23 -> 26 ; +22 [label="22: Constructor Init \n n$3=*&this:class Z * [line 27]\n n$4=*&__param_0:class Z & [line 27]\n n$5=*n$4.a:int [line 27]\n *n$3.a:int =n$5 [line 27]\n REMOVE_TEMPS(n$3,n$4,n$5); [line 27]\n " shape="box"] + + + 22 -> 21 ; +21 [label="21: Constructor Init \n n$0=*&this:class Z * [line 27]\n n$1=*&__param_0:class Z & [line 27]\n n$2=*n$1.b:int [line 27]\n *n$0.b:int =n$2 [line 27]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 27]\n NULLIFY(&__param_0,false); [line 27]\n NULLIFY(&this,false); [line 27]\n APPLY_ABSTRACTION; [line 27]\n " shape="box"] + + + 21 -> 20 ; +20 [label="20: Exit Z_Z \n " color=yellow style=filled] + + +19 [label="19: Start Z_Z\nFormals: this:class Z * __param_0:class Z &\nLocals: \n DECLARE_LOCALS(&return); [line 27]\n " color=yellow style=filled] + + + 19 -> 22 ; +18 [label="18: Exit Z_Z \n " color=yellow style=filled] + + +17 [label="17: Start Z_Z\nFormals: this:class Z *\nLocals: \n DECLARE_LOCALS(&return); [line 27]\n NULLIFY(&this,false); [line 27]\n " color=yellow style=filled] + + + 17 -> 18 ; +16 [label="16: DeclStmt \n _fun_Person_Person(&SIL_materialize_temp__n$4:class Person *) [line 23]\n _fun_Person_Person(&arr[0][0]:class Person *,&SIL_materialize_temp__n$4:class Person &) [line 23]\n _fun_Person_Person(&SIL_materialize_temp__n$3:class Person *) [line 23]\n _fun_Person_Person(&arr[0][1]:class Person *,&SIL_materialize_temp__n$3:class Person &) [line 23]\n _fun_Person_Person(&SIL_materialize_temp__n$2:class Person *) [line 23]\n _fun_Person_Person(&arr[1][0]:class Person *,&SIL_materialize_temp__n$2:class Person &) [line 23]\n _fun_Person_Person(&SIL_materialize_temp__n$1:class Person *) [line 23]\n _fun_Person_Person(&arr[1][1]:class Person *,&SIL_materialize_temp__n$1:class Person &) [line 23]\n " shape="box"] + + + 16 -> 15 ; +15 [label="15: Return Stmt \n n$0=*&arr[0][1].x:int [line 24]\n *&return:int =n$0 [line 24]\n REMOVE_TEMPS(n$0); [line 24]\n NULLIFY(&SIL_materialize_temp__n$1,false); [line 24]\n NULLIFY(&SIL_materialize_temp__n$2,false); [line 24]\n NULLIFY(&SIL_materialize_temp__n$3,false); [line 24]\n NULLIFY(&SIL_materialize_temp__n$4,false); [line 24]\n NULLIFY(&arr,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit matrix_of_person \n " color=yellow style=filled] + + +13 [label="13: Start matrix_of_person\nFormals: \nLocals: arr:class Person [2][2] SIL_materialize_temp__n$1:class Person SIL_materialize_temp__n$2:class Person SIL_materialize_temp__n$3:class Person SIL_materialize_temp__n$4:class Person \n DECLARE_LOCALS(&return,&arr,&SIL_materialize_temp__n$1,&SIL_materialize_temp__n$2,&SIL_materialize_temp__n$3,&SIL_materialize_temp__n$4); [line 22]\n " color=yellow style=filled] + + + 13 -> 16 ; +12 [label="12: DeclStmt \n _fun_Person_Person(&SIL_materialize_temp__n$3:class Person *) [line 18]\n _fun_Person_Person(&arr[0]:class Person *,&SIL_materialize_temp__n$3:class Person &) [line 18]\n _fun_Person_Person(&SIL_materialize_temp__n$2:class Person *) [line 18]\n _fun_Person_Person(&arr[1]:class Person *,&SIL_materialize_temp__n$2:class Person &) [line 18]\n _fun_Person_Person(&SIL_materialize_temp__n$1:class Person *) [line 18]\n _fun_Person_Person(&arr[2]:class Person *,&SIL_materialize_temp__n$1:class Person &) [line 18]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Return Stmt \n n$0=*&arr[0].x:int [line 19]\n *&return:int =n$0 [line 19]\n REMOVE_TEMPS(n$0); [line 19]\n NULLIFY(&SIL_materialize_temp__n$1,false); [line 19]\n NULLIFY(&SIL_materialize_temp__n$2,false); [line 19]\n NULLIFY(&SIL_materialize_temp__n$3,false); [line 19]\n NULLIFY(&arr,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 11 -> 10 ; +10 [label="10: Exit array_of_person \n " color=yellow style=filled] + + +9 [label="9: Start array_of_person\nFormals: \nLocals: arr:class Person [10] SIL_materialize_temp__n$1:class Person SIL_materialize_temp__n$2:class Person SIL_materialize_temp__n$3:class Person \n DECLARE_LOCALS(&return,&arr,&SIL_materialize_temp__n$1,&SIL_materialize_temp__n$2,&SIL_materialize_temp__n$3); [line 17]\n " color=yellow style=filled] + + + 9 -> 12 ; +8 [label="8: Constructor Init \n n$0=*&this:class Person * [line 10]\n n$1=*&__param_0:class Person & [line 10]\n n$2=*n$1.x:int [line 10]\n *n$0.x:int =n$2 [line 10]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 10]\n NULLIFY(&__param_0,false); [line 10]\n NULLIFY(&this,false); [line 10]\n APPLY_ABSTRACTION; [line 10]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Exit Person_Person \n " color=yellow style=filled] + + +6 [label="6: Start Person_Person\nFormals: this:class Person * __param_0:class Person &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 6 -> 8 ; +5 [label="5: Exit Person_Person \n " color=yellow style=filled] + + +4 [label="4: Start Person_Person\nFormals: this:class Person *\nLocals: \n DECLARE_LOCALS(&return); [line 13]\n NULLIFY(&this,false); [line 13]\n " color=yellow style=filled] + + + 4 -> 5 ; +3 [label="3: BinaryOperatorStmt: Assign \n n$0=*&this:class Person * [line 12]\n n$1=*&i:int [line 12]\n *n$0.x:int =n$1 [line 12]\n REMOVE_TEMPS(n$0,n$1); [line 12]\n NULLIFY(&i,false); [line 12]\n NULLIFY(&this,false); [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit Person_Person \n " color=yellow style=filled] + + +1 [label="1: Start Person_Person\nFormals: this:class Person * i:int \nLocals: \n DECLARE_LOCALS(&return); [line 12]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/codetoanalyze/objc/frontend/vardecl/initlist.dot b/infer/tests/codetoanalyze/objc/frontend/vardecl/initlist.dot index 389680b03..7c834de8c 100644 --- a/infer/tests/codetoanalyze/objc/frontend/vardecl/initlist.dot +++ b/infer/tests/codetoanalyze/objc/frontend/vardecl/initlist.dot @@ -7,7 +7,7 @@ digraph iCFG { 7 -> 6 ; -6 [label="6: DeclStmt \n n$0=*&c1:class C * [line 24]\n n$1=_fun_NSObject_init(n$0:class C *) virtual [line 24]\n n$2=*&c1:class C * [line 24]\n n$3=*&c2:class C * [line 24]\n *&a[0]:class C *=n$1 [line 24]\n *&a[1]:class C *=n$2 [line 24]\n *&a[2]:class C *=n$3 [line 24]\n REMOVE_TEMPS(n$0,n$1,n$2,n$3); [line 24]\n NULLIFY(&c1,false); [line 24]\n NULLIFY(&c2,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] +6 [label="6: DeclStmt \n n$2=*&c1:class C * [line 24]\n n$3=_fun_NSObject_init(n$2:class C *) virtual [line 24]\n n$1=*&c1:class C * [line 24]\n n$0=*&c2:class C * [line 24]\n *&a[0]:class C *=n$3 [line 24]\n *&a[1]:class C *=n$1 [line 24]\n *&a[2]:class C *=n$0 [line 24]\n REMOVE_TEMPS(n$2,n$3,n$1,n$0); [line 24]\n NULLIFY(&c1,false); [line 24]\n NULLIFY(&c2,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] 6 -> 5 ; diff --git a/infer/tests/frontend/cpp/ConstructorsTest.java b/infer/tests/frontend/cpp/ConstructorsTest.java index 2324278fe..79a763932 100644 --- a/infer/tests/frontend/cpp/ConstructorsTest.java +++ b/infer/tests/frontend/cpp/ConstructorsTest.java @@ -82,4 +82,10 @@ public class ConstructorsTest { throws InterruptedException, IOException, InferException { frontendTest("constructor_new.cpp"); } + + @Test + public void testConstructorArrayFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("constructor_array.cpp"); + } }