From 3699b9c0dcf788b1f62475c186cd7eb9e5776f69 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Thu, 26 May 2016 05:44:48 -0700 Subject: [PATCH] Fix translation of passing struct parameters by value Summary: Pass object by reference every time struct object is passed by value in C++. Do it only for C++/objC++ where we have guarantee that the object which is passed will be temporary one (created by copy constructor). Reviewed By: jberdine Differential Revision: D3346271 fbshipit-source-id: d3e5daa --- infer/src/clang/ast_expressions.ml | 2 + infer/src/clang/ast_expressions.mli | 2 + infer/src/clang/cMethod_trans.ml | 7 +- infer/src/clang/cTrans.ml | 17 +- infer/src/clang/cType_to_sil_type.ml | 3 + infer/src/clang/clang_ast_types.ml | 2 + .../constructor_struct_init_list.cpp.dot | 4 +- .../constructors/std_init_list.cpp.dot | 14 +- .../cpp/frontend/loops/foreach1.cpp.dot | 4 +- .../frontend/types/inheritance_field.cpp.dot | 32 ++-- .../frontend/types/struct_pass_by_value.cpp | 55 ++++++ .../types/struct_pass_by_value.cpp.dot | 177 ++++++++++++++++++ .../endtoend/cpp/StructPassByValueTest.java | 66 +++++++ infer/tests/frontend/cpp/TypesTest.java | 6 + 14 files changed, 358 insertions(+), 33 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp create mode 100644 infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp.dot create mode 100644 infer/tests/endtoend/cpp/StructPassByValueTest.java diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index d22aa3da9..84764a548 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -107,6 +107,8 @@ let create_struct_type struct_name = `StructType struct_name let create_pointer_type typ = `PointerOf typ +let create_reference_type typ = `ReferenceOf typ + let create_integer_literal n = let stmt_info = dummy_stmt_info () in let expr_info = { diff --git a/infer/src/clang/ast_expressions.mli b/infer/src/clang/ast_expressions.mli index 81f1d7877..85ffce5a0 100644 --- a/infer/src/clang/ast_expressions.mli +++ b/infer/src/clang/ast_expressions.mli @@ -45,6 +45,8 @@ val create_struct_type : string -> type_ptr val create_pointer_type : type_ptr -> type_ptr +val create_reference_type : type_ptr -> type_ptr + val make_objc_ivar_decl : decl_info -> type_ptr -> named_decl_info -> decl val make_stmt_info : decl_info -> stmt_info diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index b64af516f..59233fcbf 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -110,7 +110,12 @@ let get_parameters tenv function_method_decl_info = match par with | Clang_ast_t.ParmVarDecl (_, name_info, type_ptr, var_decl_info) -> let name = General_utils.get_var_name_string name_info var_decl_info in - (name, type_ptr) + let param_typ = CTypes_decl.type_ptr_to_sil_type tenv type_ptr in + let type_ptr' = match param_typ with + | Sil.Tstruct _ when General_utils.is_cpp_translation Config.clang_lang -> + Ast_expressions.create_reference_type type_ptr + | _ -> type_ptr in + (name, type_ptr') | _ -> assert false in let pars = IList.map par_to_ms_par (get_param_decls function_method_decl_info) in get_class_param function_method_decl_info @ pars @ get_return_param tenv function_method_decl_info diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index b2154c077..c7cbdfcce 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -475,7 +475,13 @@ struct let open CContext in let context = trans_state.context in let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in - let typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in + let ast_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in + let typ = match ast_typ with + | Sil.Tstruct _ when decl_ref.Clang_ast_t.dr_kind = `ParmVar -> + if General_utils.is_cpp_translation Config.clang_lang then + Sil.Tptr (ast_typ, Sil.Pk_reference) + else ast_typ + | _ -> ast_typ in let procname = Cfg.Procdesc.get_proc_name context.procdesc in let sil_loc = CLocation.get_sil_location stmt_info context in let pvar = CVar_decl.sil_var_of_decl_ref context decl_ref procname in @@ -492,10 +498,11 @@ struct else [(e, typ)] in Printing.log_out "\n\n PVAR ='%s'\n\n" (Pvar.to_string pvar); let res_trans = { empty_res_trans with exps = exps } in - if CTypes.is_reference_type type_ptr then - (* dereference pvar due to the behavior of reference types in clang's AST *) - dereference_value_from_result sil_loc res_trans ~strip_pointer:true - else res_trans + match typ with + | Sil.Tptr (_, Sil.Pk_reference) -> + (* dereference pvar due to the behavior of reference types in clang's AST *) + dereference_value_from_result sil_loc res_trans ~strip_pointer:true + | _ -> res_trans let field_deref_trans trans_state stmt_info pre_trans_result decl_ref ~is_constructor_init = let open CContext in diff --git a/infer/src/clang/cType_to_sil_type.ml b/infer/src/clang/cType_to_sil_type.ml index 6e88963b7..e988583f5 100644 --- a/infer/src/clang/cType_to_sil_type.ml +++ b/infer/src/clang/cType_to_sil_type.ml @@ -177,6 +177,9 @@ and type_ptr_to_sil_type translate_decl tenv type_ptr = | `PointerOf typ -> let sil_typ = type_ptr_to_sil_type translate_decl tenv typ in Sil.Tptr (sil_typ, Sil.Pk_pointer) + | `ReferenceOf typ -> + let sil_typ = type_ptr_to_sil_type translate_decl tenv typ in + Sil.Tptr (sil_typ, Sil.Pk_reference) | `ClassType (name, lang) -> let kind = match lang with `OBJC -> Csu.Objc | `CPP -> Csu.CPP in Sil.Tvar (CTypes.mk_classname name kind) diff --git a/infer/src/clang/clang_ast_types.ml b/infer/src/clang/clang_ast_types.ml index a615f8732..d17881e5e 100644 --- a/infer/src/clang/clang_ast_types.ml +++ b/infer/src/clang/clang_ast_types.ml @@ -23,6 +23,7 @@ type t_ptr = [ | `TPtr of int | `Prebuilt of int | `PointerOf of t_ptr + | `ReferenceOf of t_ptr | `ClassType of class_info | `StructType of string | `DeclPtr of int @@ -39,6 +40,7 @@ let rec type_ptr_to_string type_ptr = match type_ptr with | `TPtr raw -> "clang_ptr_" ^ (string_of_int raw) | `Prebuilt raw -> "prebuilt_" ^ (string_of_int raw) | `PointerOf typ -> "pointer_of_" ^ type_ptr_to_string typ + | `ReferenceOf typ -> "reference_of_" ^ type_ptr_to_string typ | `ClassType (name, _) -> "class_name_" ^ name | `StructType name -> "struct_name_" ^ name | `DeclPtr raw -> "decl_ptr_" ^ (string_of_int raw) diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_struct_init_list.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_struct_init_list.cpp.dot index e1705bb7f..6b9bfcb90 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_struct_init_list.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/constructor_struct_init_list.cpp.dot @@ -10,14 +10,14 @@ digraph iCFG { 4 -> 6 ; -3 [label="3: Constructor Init \n n$0=*&this:class Person * [line 14]\n n$1=*&l.top:int [line 14]\n *n$0.age:int =n$1 [line 14]\n REMOVE_TEMPS(n$0,n$1); [line 14]\n NULLIFY(&l); [line 14]\n NULLIFY(&this); [line 14]\n APPLY_ABSTRACTION; [line 14]\n " shape="box"] +3 [label="3: Constructor Init \n n$0=*&this:class Person * [line 14]\n n$1=*&l:class Insets & [line 14]\n n$2=*n$1.top:int [line 14]\n *n$0.age:int =n$2 [line 14]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 14]\n NULLIFY(&l); [line 14]\n NULLIFY(&this); [line 14]\n APPLY_ABSTRACTION; [line 14]\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 * l:class Insets \nLocals: \n DECLARE_LOCALS(&return); [line 14]\n " color=yellow style=filled] +1 [label="1: Start Person_Person\nFormals: this:class Person * l:class Insets &\nLocals: \n DECLARE_LOCALS(&return); [line 14]\n " color=yellow style=filled] 1 -> 3 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/std_init_list.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/constructors/std_init_list.cpp.dot index 829dcf2df..3fe94770c 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/constructors/std_init_list.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/std_init_list.cpp.dot @@ -10,24 +10,24 @@ digraph iCFG { 9 -> 11 ; -8 [label="8: Prune (false branch) \n PRUNE(((n$3 != n$5) == 0), false); [line 15]\n REMOVE_TEMPS(n$3,n$5); [line 15]\n NULLIFY(&i); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="invhouse"] +8 [label="8: Prune (false branch) \n PRUNE(((n$4 != n$7) == 0), false); [line 15]\n REMOVE_TEMPS(n$4,n$7); [line 15]\n NULLIFY(&i); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="invhouse"] 8 -> 2 ; -7 [label="7: Prune (true branch) \n PRUNE(((n$3 != n$5) != 0), true); [line 15]\n REMOVE_TEMPS(n$3,n$5); [line 15]\n " shape="invhouse"] +7 [label="7: Prune (true branch) \n PRUNE(((n$4 != n$7) != 0), true); [line 15]\n REMOVE_TEMPS(n$4,n$7); [line 15]\n " shape="invhouse"] 7 -> 5 ; -6 [label="6: BinaryOperatorStmt: NE \n n$3=*&i:int * [line 15]\n n$4=*&list:class std::initializer_list [line 15]\n n$5=_fun_std::initializer_list_end(&list:class std::initializer_list &) [line 15]\n REMOVE_TEMPS(n$4); [line 15]\n " shape="box"] +6 [label="6: BinaryOperatorStmt: NE \n n$4=*&i:int * [line 15]\n n$5=*&list:class std::initializer_list & [line 15]\n n$6=*n$5:class std::initializer_list [line 15]\n n$7=_fun_std::initializer_list_end(n$5:class std::initializer_list &) [line 15]\n REMOVE_TEMPS(n$5,n$6); [line 15]\n " shape="box"] 6 -> 7 ; 6 -> 8 ; -5 [label="5: UnaryOperator \n n$2=*&i:int * [line 15]\n *&i:int *=(n$2 + 1) [line 15]\n REMOVE_TEMPS(n$2); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] +5 [label="5: UnaryOperator \n n$3=*&i:int * [line 15]\n *&i:int *=(n$3 + 1) [line 15]\n REMOVE_TEMPS(n$3); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] 5 -> 3 ; -4 [label="4: DeclStmt \n n$0=*&list:class std::initializer_list [line 15]\n n$1=_fun_std::initializer_list_begin(&list:class std::initializer_list &) [line 15]\n *&i:int *=n$1 [line 15]\n REMOVE_TEMPS(n$0,n$1); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] +4 [label="4: DeclStmt \n n$0=*&list:class std::initializer_list & [line 15]\n n$1=*n$0:class std::initializer_list [line 15]\n n$2=_fun_std::initializer_list_begin(n$0:class std::initializer_list &) [line 15]\n *&i:int *=n$2 [line 15]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] 4 -> 3 ; @@ -35,10 +35,10 @@ digraph iCFG { 3 -> 6 ; -2 [label="2: Exit X_X \n NULLIFY(&list); [line 18]\n " color=yellow style=filled] +2 [label="2: Exit X_X \n " color=yellow style=filled] -1 [label="1: Start X_X\nFormals: this:class X * list:class std::initializer_list \nLocals: i:int * \n DECLARE_LOCALS(&return,&i); [line 14]\n " color=yellow style=filled] +1 [label="1: Start X_X\nFormals: this:class X * list:class std::initializer_list &\nLocals: i:int * \n DECLARE_LOCALS(&return,&i); [line 14]\n " color=yellow style=filled] 1 -> 4 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/loops/foreach1.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/loops/foreach1.cpp.dot index 7272430a9..a7972c779 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/loops/foreach1.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/loops/foreach1.cpp.dot @@ -96,14 +96,14 @@ digraph iCFG { 19 -> 24 ; -18 [label="18: Return Stmt \n n$0=*&i1.val:int [line 21]\n n$1=*&i2.val:int [line 21]\n *&return:_Bool =(n$0 != n$1) [line 21]\n REMOVE_TEMPS(n$0,n$1); [line 21]\n NULLIFY(&i1); [line 21]\n NULLIFY(&i2); [line 21]\n APPLY_ABSTRACTION; [line 21]\n " shape="box"] +18 [label="18: Return Stmt \n n$0=*&i1:class iterator & [line 21]\n n$1=*n$0.val:int [line 21]\n n$2=*&i2:class iterator & [line 21]\n n$3=*n$2.val:int [line 21]\n *&return:_Bool =(n$1 != n$3) [line 21]\n REMOVE_TEMPS(n$0,n$1,n$2,n$3); [line 21]\n NULLIFY(&i1); [line 21]\n NULLIFY(&i2); [line 21]\n APPLY_ABSTRACTION; [line 21]\n " shape="box"] 18 -> 17 ; 17 [label="17: Exit operator!= \n " color=yellow style=filled] -16 [label="16: Start operator!=\nFormals: i1:class iterator i2:class iterator \nLocals: \n DECLARE_LOCALS(&return); [line 21]\n " color=yellow style=filled] +16 [label="16: Start operator!=\nFormals: i1:class iterator & i2:class iterator &\nLocals: \n DECLARE_LOCALS(&return); [line 21]\n " color=yellow style=filled] 16 -> 18 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot index ace0424f8..685b85e8c 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/types/inheritance_field.cpp.dot @@ -18,18 +18,18 @@ digraph iCFG { 37 -> 41 ; -36 [label="36: BinaryOperatorStmt: Assign \n *&s.b1:int =1 [line 62]\n " shape="box"] +36 [label="36: BinaryOperatorStmt: Assign \n n$2=*&s:class Sub & [line 62]\n *n$2.b1:int =1 [line 62]\n REMOVE_TEMPS(n$2); [line 62]\n " shape="box"] 36 -> 35 ; -35 [label="35: Return Stmt \n n$0=*&s.b1:int [line 63]\n *&return:int =(1 / n$0) [line 63]\n REMOVE_TEMPS(n$0); [line 63]\n NULLIFY(&s); [line 63]\n APPLY_ABSTRACTION; [line 63]\n " shape="box"] +35 [label="35: Return Stmt \n n$0=*&s:class Sub & [line 63]\n n$1=*n$0.b1:int [line 63]\n *&return:int =(1 / n$1) [line 63]\n REMOVE_TEMPS(n$0,n$1); [line 63]\n NULLIFY(&s); [line 63]\n APPLY_ABSTRACTION; [line 63]\n " shape="box"] 35 -> 34 ; 34 [label="34: Exit div1_b1 \n " color=yellow style=filled] -33 [label="33: Start div1_b1\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 61]\n " color=yellow style=filled] +33 [label="33: Start div1_b1\nFormals: s:class Sub &\nLocals: \n DECLARE_LOCALS(&return); [line 61]\n " color=yellow style=filled] 33 -> 36 ; @@ -71,11 +71,11 @@ digraph iCFG { 23 -> 27 ; -22 [label="22: BinaryOperatorStmt: Assign \n *&s.b1:int =0 [line 44]\n " shape="box"] +22 [label="22: BinaryOperatorStmt: Assign \n n$3=*&s:class Sub & [line 44]\n *n$3.b1:int =0 [line 44]\n REMOVE_TEMPS(n$3); [line 44]\n " shape="box"] 22 -> 21 ; -21 [label="21: DeclStmt \n *&b:class Sub &=&s [line 45]\n " shape="box"] +21 [label="21: DeclStmt \n n$2=*&s:class Sub & [line 45]\n *&b:class Sub &=n$2 [line 45]\n REMOVE_TEMPS(n$2); [line 45]\n NULLIFY(&s); [line 45]\n " shape="box"] 21 -> 20 ; @@ -83,10 +83,10 @@ digraph iCFG { 20 -> 19 ; -19 [label="19: Exit div0_cast_ref \n NULLIFY(&s); [line 47]\n " color=yellow style=filled] +19 [label="19: Exit div0_cast_ref \n " color=yellow style=filled] -18 [label="18: Start div0_cast_ref\nFormals: s:class Sub \nLocals: b:class Base1 & \n DECLARE_LOCALS(&return,&b); [line 43]\n " color=yellow style=filled] +18 [label="18: Start div0_cast_ref\nFormals: s:class Sub &\nLocals: b:class Base1 & \n DECLARE_LOCALS(&return,&b); [line 43]\n " color=yellow style=filled] 18 -> 22 ; @@ -109,48 +109,48 @@ digraph iCFG { 13 -> 17 ; -12 [label="12: BinaryOperatorStmt: Assign \n *&s.s:int =0 [line 33]\n " shape="box"] +12 [label="12: BinaryOperatorStmt: Assign \n n$2=*&s:class Sub & [line 33]\n *n$2.s:int =0 [line 33]\n REMOVE_TEMPS(n$2); [line 33]\n " shape="box"] 12 -> 11 ; -11 [label="11: Return Stmt \n n$0=*&s.s:int [line 34]\n *&return:int =(1 / n$0) [line 34]\n REMOVE_TEMPS(n$0); [line 34]\n NULLIFY(&s); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] +11 [label="11: Return Stmt \n n$0=*&s:class Sub & [line 34]\n n$1=*n$0.s:int [line 34]\n *&return:int =(1 / n$1) [line 34]\n REMOVE_TEMPS(n$0,n$1); [line 34]\n NULLIFY(&s); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] 11 -> 10 ; 10 [label="10: Exit div0_s \n " color=yellow style=filled] -9 [label="9: Start div0_s\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 32]\n " color=yellow style=filled] +9 [label="9: Start div0_s\nFormals: s:class Sub &\nLocals: \n DECLARE_LOCALS(&return); [line 32]\n " color=yellow style=filled] 9 -> 12 ; -8 [label="8: BinaryOperatorStmt: Assign \n *&s.b2:int =0 [line 28]\n " shape="box"] +8 [label="8: BinaryOperatorStmt: Assign \n n$2=*&s:class Sub & [line 28]\n *n$2.b2:int =0 [line 28]\n REMOVE_TEMPS(n$2); [line 28]\n " shape="box"] 8 -> 7 ; -7 [label="7: Return Stmt \n n$0=*&s.b2:int [line 29]\n *&return:int =(1 / n$0) [line 29]\n REMOVE_TEMPS(n$0); [line 29]\n NULLIFY(&s); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] +7 [label="7: Return Stmt \n n$0=*&s:class Sub & [line 29]\n n$1=*n$0.b2:int [line 29]\n *&return:int =(1 / n$1) [line 29]\n REMOVE_TEMPS(n$0,n$1); [line 29]\n NULLIFY(&s); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] 7 -> 6 ; 6 [label="6: Exit div0_b2 \n " color=yellow style=filled] -5 [label="5: Start div0_b2\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 27]\n " color=yellow style=filled] +5 [label="5: Start div0_b2\nFormals: s:class Sub &\nLocals: \n DECLARE_LOCALS(&return); [line 27]\n " color=yellow style=filled] 5 -> 8 ; -4 [label="4: BinaryOperatorStmt: Assign \n *&s.b1:int =0 [line 23]\n " shape="box"] +4 [label="4: BinaryOperatorStmt: Assign \n n$2=*&s:class Sub & [line 23]\n *n$2.b1:int =0 [line 23]\n REMOVE_TEMPS(n$2); [line 23]\n " shape="box"] 4 -> 3 ; -3 [label="3: Return Stmt \n n$0=*&s.b1:int [line 24]\n *&return:int =(1 / n$0) [line 24]\n REMOVE_TEMPS(n$0); [line 24]\n NULLIFY(&s); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] +3 [label="3: Return Stmt \n n$0=*&s:class Sub & [line 24]\n n$1=*n$0.b1:int [line 24]\n *&return:int =(1 / n$1) [line 24]\n REMOVE_TEMPS(n$0,n$1); [line 24]\n NULLIFY(&s); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] 3 -> 2 ; 2 [label="2: Exit div0_b1 \n " color=yellow style=filled] -1 [label="1: Start div0_b1\nFormals: s:class Sub \nLocals: \n DECLARE_LOCALS(&return); [line 22]\n " color=yellow style=filled] +1 [label="1: Start div0_b1\nFormals: s:class Sub &\nLocals: \n DECLARE_LOCALS(&return); [line 22]\n " color=yellow style=filled] 1 -> 4 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp b/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp new file mode 100644 index 000000000..26a724ec8 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp @@ -0,0 +1,55 @@ +/* + * 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. + */ + +struct X { + int f; + X(int f) : f(f) {} +}; + +struct Y { + Y(const X& x) : x(x) {} + X x; +}; + +int get_f(X val) { return val.f; } + +// val is passed by value, so it's not really a setter +void set_f(X val, int f) { val.f = f; } + +int var_div0() { + X x(0); + return 1 / get_f(x); +} + +int var_div1() { + X x(1); + return 1 / get_f(x); +} + +int temp_div0() { return 1 / get_f(X(0)); } + +int temp_div1() { return 1 / get_f(X(1)); } + +int field_div0() { + X x(0); + Y y(x); + return 1 / get_f(y.x); +} + +int param_get_copied_div0() { + X x(0); + set_f(x, 1); // this won't change x + return 1 / x.f; +} + +int param_get_copied_div1() { + X x(1); + set_f(x, 0); // this won't change x + return 1 / x.f; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp.dot new file mode 100644 index 000000000..9c1b0fca0 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp.dot @@ -0,0 +1,177 @@ +digraph iCFG { +47 [label="47: DeclStmt \n _fun_X_X(&x:class X *,1:int ) [line 52]\n " shape="box"] + + + 47 -> 46 ; +46 [label="46: Call _fun_set_f \n _fun_X_X(&__temp_construct_n$1:class X *,&x:class X &) [line 53]\n _fun_set_f(&__temp_construct_n$1:class X ,0:int ) [line 53]\n " shape="box"] + + + 46 -> 45 ; +45 [label="45: Return Stmt \n n$0=*&x.f:int [line 54]\n *&return:int =(1 / n$0) [line 54]\n REMOVE_TEMPS(n$0); [line 54]\n APPLY_ABSTRACTION; [line 54]\n " shape="box"] + + + 45 -> 44 ; +44 [label="44: Exit param_get_copied_div1 \n NULLIFY(&x); [line 55]\n NULLIFY(&__temp_construct_n$1); [line 55]\n " color=yellow style=filled] + + +43 [label="43: Start param_get_copied_div1\nFormals: \nLocals: __temp_construct_n$1:class X x:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$1,&x); [line 51]\n " color=yellow style=filled] + + + 43 -> 47 ; +42 [label="42: DeclStmt \n _fun_X_X(&x:class X *,0:int ) [line 46]\n " shape="box"] + + + 42 -> 41 ; +41 [label="41: Call _fun_set_f \n _fun_X_X(&__temp_construct_n$1:class X *,&x:class X &) [line 47]\n _fun_set_f(&__temp_construct_n$1:class X ,1:int ) [line 47]\n " shape="box"] + + + 41 -> 40 ; +40 [label="40: Return Stmt \n n$0=*&x.f:int [line 48]\n *&return:int =(1 / n$0) [line 48]\n REMOVE_TEMPS(n$0); [line 48]\n APPLY_ABSTRACTION; [line 48]\n " shape="box"] + + + 40 -> 39 ; +39 [label="39: Exit param_get_copied_div0 \n NULLIFY(&x); [line 49]\n NULLIFY(&__temp_construct_n$1); [line 49]\n " color=yellow style=filled] + + +38 [label="38: Start param_get_copied_div0\nFormals: \nLocals: __temp_construct_n$1:class X x:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$1,&x); [line 45]\n " color=yellow style=filled] + + + 38 -> 42 ; +37 [label="37: DeclStmt \n _fun_X_X(&x:class X *,0:int ) [line 40]\n " shape="box"] + + + 37 -> 36 ; +36 [label="36: DeclStmt \n _fun_Y_Y(&y:class Y *,&x:class X &) [line 41]\n " shape="box"] + + + 36 -> 35 ; +35 [label="35: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,&y.x:class X &) [line 42]\n n$1=_fun_get_f(&__temp_construct_n$0:class X ) [line 42]\n *&return:int =(1 / n$1) [line 42]\n REMOVE_TEMPS(n$1); [line 42]\n APPLY_ABSTRACTION; [line 42]\n " shape="box"] + + + 35 -> 34 ; +34 [label="34: Exit field_div0 \n NULLIFY(&y); [line 43]\n NULLIFY(&x); [line 43]\n NULLIFY(&__temp_construct_n$0); [line 43]\n " color=yellow style=filled] + + +33 [label="33: Start field_div0\nFormals: \nLocals: __temp_construct_n$0:class X y:class Y x:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0,&y,&x); [line 39]\n " color=yellow style=filled] + + + 33 -> 37 ; +32 [label="32: Return Stmt \n _fun_X_X(&SIL_materialize_temp__n$1:class X *,1:int ) [line 37]\n _fun_X_X(&__temp_construct_n$0:class X *,&SIL_materialize_temp__n$1:class X &) [line 37]\n n$2=_fun_get_f(&__temp_construct_n$0:class X ) [line 37]\n *&return:int =(1 / n$2) [line 37]\n REMOVE_TEMPS(n$2); [line 37]\n APPLY_ABSTRACTION; [line 37]\n " shape="box"] + + + 32 -> 31 ; +31 [label="31: Exit temp_div1 \n NULLIFY(&__temp_construct_n$0); [line 37]\n NULLIFY(&SIL_materialize_temp__n$1); [line 37]\n " color=yellow style=filled] + + +30 [label="30: Start temp_div1\nFormals: \nLocals: __temp_construct_n$0:class X SIL_materialize_temp__n$1:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0,&SIL_materialize_temp__n$1); [line 37]\n " color=yellow style=filled] + + + 30 -> 32 ; +29 [label="29: Return Stmt \n _fun_X_X(&SIL_materialize_temp__n$1:class X *,0:int ) [line 35]\n _fun_X_X(&__temp_construct_n$0:class X *,&SIL_materialize_temp__n$1:class X &) [line 35]\n n$2=_fun_get_f(&__temp_construct_n$0:class X ) [line 35]\n *&return:int =(1 / n$2) [line 35]\n REMOVE_TEMPS(n$2); [line 35]\n APPLY_ABSTRACTION; [line 35]\n " shape="box"] + + + 29 -> 28 ; +28 [label="28: Exit temp_div0 \n NULLIFY(&__temp_construct_n$0); [line 35]\n NULLIFY(&SIL_materialize_temp__n$1); [line 35]\n " color=yellow style=filled] + + +27 [label="27: Start temp_div0\nFormals: \nLocals: __temp_construct_n$0:class X SIL_materialize_temp__n$1:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0,&SIL_materialize_temp__n$1); [line 35]\n " color=yellow style=filled] + + + 27 -> 29 ; +26 [label="26: DeclStmt \n _fun_X_X(&x:class X *,1:int ) [line 31]\n " shape="box"] + + + 26 -> 25 ; +25 [label="25: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,&x:class X &) [line 32]\n n$1=_fun_get_f(&__temp_construct_n$0:class X ) [line 32]\n *&return:int =(1 / n$1) [line 32]\n REMOVE_TEMPS(n$1); [line 32]\n APPLY_ABSTRACTION; [line 32]\n " shape="box"] + + + 25 -> 24 ; +24 [label="24: Exit var_div1 \n NULLIFY(&x); [line 33]\n NULLIFY(&__temp_construct_n$0); [line 33]\n " color=yellow style=filled] + + +23 [label="23: Start var_div1\nFormals: \nLocals: __temp_construct_n$0:class X x:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0,&x); [line 30]\n " color=yellow style=filled] + + + 23 -> 26 ; +22 [label="22: DeclStmt \n _fun_X_X(&x:class X *,0:int ) [line 26]\n " shape="box"] + + + 22 -> 21 ; +21 [label="21: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,&x:class X &) [line 27]\n n$1=_fun_get_f(&__temp_construct_n$0:class X ) [line 27]\n *&return:int =(1 / n$1) [line 27]\n REMOVE_TEMPS(n$1); [line 27]\n APPLY_ABSTRACTION; [line 27]\n " shape="box"] + + + 21 -> 20 ; +20 [label="20: Exit var_div0 \n NULLIFY(&x); [line 28]\n NULLIFY(&__temp_construct_n$0); [line 28]\n " color=yellow style=filled] + + +19 [label="19: Start var_div0\nFormals: \nLocals: __temp_construct_n$0:class X x:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0,&x); [line 25]\n " color=yellow style=filled] + + + 19 -> 22 ; +18 [label="18: BinaryOperatorStmt: Assign \n n$0=*&val:class X & [line 23]\n n$1=*&f:int [line 23]\n *n$0.f:int =n$1 [line 23]\n REMOVE_TEMPS(n$0,n$1); [line 23]\n NULLIFY(&f); [line 23]\n NULLIFY(&val); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] + + + 18 -> 17 ; +17 [label="17: Exit set_f \n " color=yellow style=filled] + + +16 [label="16: Start set_f\nFormals: val:class X & f:int \nLocals: \n DECLARE_LOCALS(&return); [line 23]\n " color=yellow style=filled] + + + 16 -> 18 ; +15 [label="15: Return Stmt \n n$0=*&val:class X & [line 20]\n n$1=*n$0.f:int [line 20]\n *&return:int =n$1 [line 20]\n REMOVE_TEMPS(n$0,n$1); [line 20]\n NULLIFY(&val); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit get_f \n " color=yellow style=filled] + + +13 [label="13: Start get_f\nFormals: val:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 20]\n " color=yellow style=filled] + + + 13 -> 15 ; +12 [label="12: Constructor Init \n n$0=*&this:class Y * [line 16]\n n$1=*&x:class X & [line 16]\n _fun_X_X(n$0.x:class X *,n$1:class X &) [line 16]\n REMOVE_TEMPS(n$0,n$1); [line 16]\n NULLIFY(&this); [line 16]\n NULLIFY(&x); [line 16]\n APPLY_ABSTRACTION; [line 16]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Exit Y_Y \n " color=yellow style=filled] + + +10 [label="10: Start Y_Y\nFormals: this:class Y * x:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 16]\n " color=yellow style=filled] + + + 10 -> 12 ; +9 [label="9: Constructor Init \n n$0=*&this:class X * [line 10]\n n$1=*&__param_0:class X & [line 10]\n n$2=*n$1.f:int [line 10]\n *n$0.f:int =n$2 [line 10]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 10]\n NULLIFY(&__param_0); [line 10]\n NULLIFY(&this); [line 10]\n APPLY_ABSTRACTION; [line 10]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: Exit X_X \n " color=yellow style=filled] + + +7 [label="7: Start X_X\nFormals: this:class X * __param_0:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 7 -> 9 ; +6 [label="6: Constructor Init \n n$0=*&this:class X * [line 10]\n n$1=*&__param_0:class X & [line 10]\n n$2=*n$1.f:int [line 10]\n *n$0.f:int =n$2 [line 10]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 10]\n NULLIFY(&__param_0); [line 10]\n NULLIFY(&this); [line 10]\n APPLY_ABSTRACTION; [line 10]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: Exit X_X \n " color=yellow style=filled] + + +4 [label="4: Start X_X\nFormals: this:class X * __param_0:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 4 -> 6 ; +3 [label="3: Constructor Init \n n$0=*&this:class X * [line 12]\n n$1=*&f:int [line 12]\n *n$0.f:int =n$1 [line 12]\n REMOVE_TEMPS(n$0,n$1); [line 12]\n NULLIFY(&f); [line 12]\n NULLIFY(&this); [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit X_X \n " color=yellow style=filled] + + +1 [label="1: Start X_X\nFormals: this:class X * f:int \nLocals: \n DECLARE_LOCALS(&return); [line 12]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/endtoend/cpp/StructPassByValueTest.java b/infer/tests/endtoend/cpp/StructPassByValueTest.java new file mode 100644 index 000000000..dd53b296d --- /dev/null +++ b/infer/tests/endtoend/cpp/StructPassByValueTest.java @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package endtoend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class StructPassByValueTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/types/struct_pass_by_value.cpp"; + + private static ImmutableList inferCmd; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsOnDiv0MethodsErrorIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + String[] procedures = { + "var_div0", + "temp_div0", + "field_div0", + "param_get_copied_div0", + }; + assertThat( + "Results should contain the expected divide by zero", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/frontend/cpp/TypesTest.java b/infer/tests/frontend/cpp/TypesTest.java index 6d8b770d8..0cd22c8ae 100644 --- a/infer/tests/frontend/cpp/TypesTest.java +++ b/infer/tests/frontend/cpp/TypesTest.java @@ -34,4 +34,10 @@ public class TypesTest { throws InterruptedException, IOException, InferException { frontendTest("typeid_expr.cpp"); } + + @Test + public void structPassByValueDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("struct_pass_by_value.cpp"); + } }