From 198c3c82e1e4fe0205a2c736f89066db26424e4e Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Mon, 10 Aug 2015 13:59:09 -0100 Subject: [PATCH] [C++ frontend] Handle calling c++ methods Summary: Translate calling C++ methods. Code is pretty similar to C function calling, but slightly simpler and has some modifications --- infer/src/clang/cTrans.ml | 112 +++++++++++++++--- infer/src/clang/cTrans_utils.ml | 4 +- infer/src/clang/cTrans_utils.mli | 3 +- .../cpp/frontend/types/methods.cpp | 32 +++-- .../cpp/frontend/types/methods.dot | 74 ++++++++---- 5 files changed, 178 insertions(+), 47 deletions(-) diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 0b5464ddc..c01883a34 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -524,7 +524,8 @@ struct let function_type = CTypes_decl.get_type_from_expr_info expr_info context.tenv in let procname = Cfg.Procdesc.get_proc_name context.procdesc in let sil_loc = get_sil_location si pln context in - let fun_exp_stmt, params_stmt = (match stmt_list with (* First stmt is the function expr and the rest are params*) + (* First stmt is the function expr and the rest are params *) + let fun_exp_stmt, params_stmt = (match stmt_list with | fe:: params -> fe, params | _ -> assert false) in let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in @@ -554,7 +555,8 @@ struct (Procname.to_string pn) <> CFrontend_config.builtin_object_size | _ -> true in let params_stmt = if should_translate_args then - CTrans_utils.assign_default_params params_stmt callee_pname_opt else [] in + CTrans_utils.assign_default_params params_stmt callee_pname_opt ~is_cxx_method:false + else [] in let res_trans_par = let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) params_stmt in let rt = collect_res_trans (res_trans_callee :: l) in @@ -609,6 +611,70 @@ struct | [ret_id'] -> { res_trans_to_parent with exps =[(Sil.Var ret_id', function_type)] } | _ -> assert false) (* by construction of red_id, we cannot be in this case *) + and cxxMemberCallExpr_trans trans_state si stmt_list expr_info = + let pln = trans_state.parent_line_number in + let context = trans_state.context in + let function_type = CTypes_decl.get_type_from_expr_info expr_info context.tenv in + let procname = Cfg.Procdesc.get_proc_name context.procdesc in + let sil_loc = get_sil_location si pln context in + (* First stmt is the method+this expr and the rest are params *) + let fun_exp_stmt, params_stmt = (match stmt_list with + | fe:: params -> fe, params + | _ -> assert false) in + let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in + (* claim priority if no ancestors has claimed priority before *) + let line_number = get_line si pln in + let trans_state_callee = { trans_state_pri with + parent_line_number = line_number; + succ_nodes = [] } in + let result_trans_callee = instruction trans_state_callee fun_exp_stmt in + + (* first for method address, second for 'this' expression *) + assert ((list_length result_trans_callee.exps) = 2); + let (sil_method, typ_method) = list_hd result_trans_callee.exps in + let callee_pname = match sil_method with + | Sil.Const (Sil.Cfun pn) -> pn + | _ -> assert false (* method pointer not implemented, this shouldn't happen *) in + + let params_stmt = CTrans_utils.assign_default_params params_stmt (Some callee_pname) ~is_cxx_method:true in + (* As we may have nodes coming from different parameters we need to *) + (* call instruction for each parameter and collect the results *) + (* afterwards. The 'instructions' function does not do that *) + let trans_state_param = + { trans_state_pri with parent_line_number = line_number; succ_nodes = [] } in + let result_trans_params = + let l = list_map (exec_with_self_exception instruction trans_state_param) params_stmt in + (* this function will automatically merge 'this' argument with rest of arguments in 'l'*) + let rt = collect_res_trans (result_trans_callee :: l) in + { rt with exps = list_tl rt.exps } in + + let actual_params = result_trans_params.exps in + let ret_id = if (Sil.typ_equal function_type Sil.Tvoid) then [] + else [Ident.create_fresh Ident.knormal] in + let call_flags = { + Sil.cf_virtual = false (* TODO t7725350 *); + Sil.cf_noreturn = false; + Sil.cf_is_objc_block = false; + } in + let call_instr = Sil.Call (ret_id, sil_method, actual_params, sil_loc, call_flags) in + let ids = result_trans_params.ids @ ret_id in + let instrs = result_trans_params.instrs @ [call_instr] in + let res_trans_tmp = { result_trans_params with ids = ids; instrs = instrs; exps =[] } in + + let nname = "Call " ^ (Sil.exp_to_string sil_method) in + let result_trans_to_parent = + PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp in + Cg.add_edge context.cg procname callee_pname; + (try + let callee_ms = CMethod_signature.find callee_pname in + ignore (CMethod_trans.create_local_procdesc context.cfg context.tenv callee_ms [] [] false) + with Not_found -> + CMethod_trans.create_external_procdesc context.cfg callee_pname false None); + match ret_id with + | [] -> { result_trans_to_parent with exps =[] } + | [ret_id'] -> { result_trans_to_parent with exps =[(Sil.Var ret_id', function_type)] } + | _ -> assert false (* by construction of red_id, we cannot be in this case *) + and objCMessageExpr_trans trans_state si obj_c_message_expr_info stmt_list expr_info = Printing.log_out " priority node free = '%s'\n@." (string_of_bool (PriorityNode.is_priority_free trans_state)); @@ -1415,29 +1481,40 @@ struct let field_name = match decl_ref.Clang_ast_t.dr_name with | Some s -> s.Clang_ast_t.ni_name | _ -> assert false in - let field_typ = match decl_ref.Clang_ast_t.dr_qual_type with - | Some t -> CTypes_decl.qual_type_to_sil_type trans_state.context.tenv t + let field_qt = match decl_ref.Clang_ast_t.dr_qual_type with + | Some t -> t | _ -> assert false in + let field_typ = CTypes_decl.qual_type_to_sil_type trans_state.context.tenv field_qt in Printing.log_out "!!!!! Dealing with field '%s' @." field_name; let exp_stmt = extract_stmt_from_singleton stmt_list "WARNING: in MemberExpr there must be only one stmt defining its expression.\n" in - let res_trans_exp_stmt = instruction trans_state exp_stmt in - let (e, class_typ) = extract_exp_from_list res_trans_exp_stmt.exps + let result_trans_exp_stmt = instruction trans_state exp_stmt in + let (obj_sil, class_typ) = extract_exp_from_list result_trans_exp_stmt.exps "WARNING: in MemberExpr we expect the translation of the stmt to return an expression\n" in let class_typ = (match class_typ with | Sil.Tptr (t, _) -> CTypes_decl.expand_structured_type trans_state.context.tenv t | t -> t) in - let exp = - (match class_typ with - | Sil.Tvoid -> Sil.exp_minus_one - | _ -> - Printing.log_out "Type is '%s' @." (Sil.typ_to_string class_typ); - ( match ObjcInterface_decl.find_field trans_state.context.tenv field_name (Some class_typ) false with - | Some (fn, _, _) -> Sil.Lfield (e, fn, class_typ) - | None -> assert false)) in - { res_trans_exp_stmt with - exps = [(exp, field_typ)] } + match decl_ref.Clang_ast_t.dr_kind with + | `Field | `ObjCIvar -> + let exp = match class_typ with + | Sil.Tvoid -> Sil.exp_minus_one + | _ -> + Printing.log_out "Type is '%s' @." (Sil.typ_to_string class_typ); + (match ObjcInterface_decl.find_field trans_state.context.tenv field_name (Some class_typ) false with + | Some (fn, _, _) -> Sil.Lfield (obj_sil, fn, class_typ) + | None -> assert false) in + { result_trans_exp_stmt with + exps = [(exp, field_typ)] } + | `CXXMethod -> + (* consider using context.CContext.is_callee_expression to deal with pointers to methods? *) + let raw_type = field_qt.Clang_ast_t.qt_raw in + let class_name = match class_typ with Sil.Tptr (t, _) | t -> CTypes.classname_of_type t in + let pname = mk_procname_from_cpp_method class_name field_name raw_type in + let method_exp = (Sil.Const (Sil.Cfun pname), field_typ) in + Cfg.set_procname_priority trans_state.context.cfg pname; + { result_trans_exp_stmt with exps = [method_exp; (obj_sil, class_typ)] } + | _ -> assert false and objCIvarRefExpr_trans trans_state stmt_info expr_info stmt_list obj_c_ivar_ref_expr_info = let decl_ref = obj_c_ivar_ref_expr_info.Clang_ast_t.ovrei_decl_ref in @@ -1702,6 +1779,9 @@ struct | None -> callExpr_trans trans_state stmt_info stmt_list ei) + | CXXMemberCallExpr(stmt_info, stmt_list, ei) -> + cxxMemberCallExpr_trans trans_state stmt_info stmt_list ei + | ObjCMessageExpr(stmt_info, stmt_list, expr_info, obj_c_message_expr_info) -> if is_block_enumerate_function obj_c_message_expr_info then block_enumeration_trans trans_state stmt_info stmt_list expr_info diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 23a8fcc4d..eef72c630 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -678,13 +678,15 @@ let is_dispatch_function stmt_list = | _ -> None)) | _ -> None -let assign_default_params params_stmt callee_pname_opt = +let assign_default_params params_stmt callee_pname_opt ~is_cxx_method = match callee_pname_opt with | None -> params_stmt | Some callee_pname -> try let callee_ms = CMethod_signature.find callee_pname in let args = CMethod_signature.ms_get_args callee_ms in + let args = if is_cxx_method then match args with _::tl -> tl | _ -> assert false + else args in let params_args = list_combine params_stmt args in let replace_default_arg param = match param with diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 85b6c8473..2f78c4747 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -203,6 +203,7 @@ val is_logical_negation_of_int : Sil.tenv -> Clang_ast_t.expr_info -> Clang_ast_ val is_dispatch_function : Clang_ast_t.stmt list -> int option -val assign_default_params : Clang_ast_t.stmt list -> Procname.t option -> Clang_ast_t.stmt list +val assign_default_params : Clang_ast_t.stmt list -> Procname.t option -> + is_cxx_method:bool -> Clang_ast_t.stmt list val is_block_enumerate_function : Clang_ast_t.obj_c_message_expr_info -> bool diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/methods.cpp b/infer/tests/codetoanalyze/cpp/frontend/types/methods.cpp index 71ccbfb94..6045f7d8c 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/types/methods.cpp +++ b/infer/tests/codetoanalyze/cpp/frontend/types/methods.cpp @@ -16,6 +16,8 @@ struct A { // inline definition int def_in() { int c = 10; return c+1;} + // default parameters + int fun_default(int a, int b = 10, int c = 20) {return a+b+c;} //static function //static int get_fun() {return 1;} }; @@ -41,15 +43,27 @@ int A::add(const A& other) { //return member1; } -void test() { - // constructing objects - //A a; - //a.fun(1,2); +void call_method() { + // constructing objects + //A a; + //a.fun(1,2); - A *a_ptr; - // calling methods - // a_ptr->fun(10,20); + A *a_ptr; + // calling methods + a_ptr->fun(10,20); + a_ptr->fun_default(1,2,3); +} + +void call_method_with_default_parameters() { + A *a_ptr; + a_ptr->fun_default(1,2); + a_ptr->fun_default(1); +} - //A::get_fun(); - //a.get_fun(); +/* +void call_static_method { + A *a_ptr; + A::get_fun(); + a_ptr->get_fun(); } +*/ diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/methods.dot b/infer/tests/codetoanalyze/cpp/frontend/types/methods.dot index 1c141e156..da185b0c8 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/types/methods.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/types/methods.dot @@ -1,51 +1,85 @@ digraph iCFG { -20 [label="20: Exit test \n " color=yellow style=filled] +29 [label="29: Call _fun_A_fun_default \n n$16=*&a_ptr:struct A * [line 59]\n n$17=_fun_A_fun_default(n$16:struct A ,1:int ,2:int ,20:int ) [line 59]\n REMOVE_TEMPS(n$16,n$17); [line 59]\n " shape="box"] -19 [label="19: Start test\nFormals: \nLocals: a_ptr:struct A * \n DECLARE_LOCALS(&return,&a_ptr); [line 44]\n NULLIFY(&a_ptr,false); [line 44]\n " color=yellow style=filled] + 29 -> 28 ; +28 [label="28: Call _fun_A_fun_default \n n$14=*&a_ptr:struct A * [line 60]\n n$15=_fun_A_fun_default(n$14:struct A ,1:int ,10:int ,20:int ) [line 60]\n REMOVE_TEMPS(n$14,n$15); [line 60]\n NULLIFY(&a_ptr,false); [line 60]\n APPLY_ABSTRACTION; [line 60]\n " shape="box"] - 19 -> 20 ; -18 [label="18: Exit A_add \n " color=yellow style=filled] + 28 -> 27 ; +27 [label="27: Exit call_method_with_default_parameters \n " color=yellow style=filled] -17 [label="17: Start A_add\nFormals: this:struct A other:struct A &\nLocals: \n DECLARE_LOCALS(&return); [line 39]\n NULLIFY(&this,false); [line 39]\n NULLIFY(&other,false); [line 39]\n " color=yellow style=filled] +26 [label="26: Start call_method_with_default_parameters\nFormals: \nLocals: a_ptr:struct A * \n DECLARE_LOCALS(&return,&a_ptr); [line 57]\n " color=yellow style=filled] - 17 -> 18 ; -16 [label="16: Return Stmt \n n$5=*&a:int [line 36]\n n$6=*&b:int [line 36]\n *&return:int =(n$5 + n$6) [line 36]\n REMOVE_TEMPS(n$5,n$6); [line 36]\n NULLIFY(&a,false); [line 36]\n NULLIFY(&b,false); [line 36]\n APPLY_ABSTRACTION; [line 36]\n " shape="box"] + 26 -> 29 ; +25 [label="25: Call _fun_A_fun \n n$12=*&a_ptr:struct A * [line 53]\n n$13=_fun_A_fun(n$12:struct A ,10:int ,20:int ) [line 53]\n REMOVE_TEMPS(n$12,n$13); [line 53]\n " shape="box"] + + + 25 -> 24 ; +24 [label="24: Call _fun_A_fun_default \n n$10=*&a_ptr:struct A * [line 54]\n n$11=_fun_A_fun_default(n$10:struct A ,1:int ,2:int ,3:int ) [line 54]\n REMOVE_TEMPS(n$10,n$11); [line 54]\n NULLIFY(&a_ptr,false); [line 54]\n APPLY_ABSTRACTION; [line 54]\n " shape="box"] + + + 24 -> 23 ; +23 [label="23: Exit call_method \n " color=yellow style=filled] + + +22 [label="22: Start call_method\nFormals: \nLocals: a_ptr:struct A * \n DECLARE_LOCALS(&return,&a_ptr); [line 46]\n " color=yellow style=filled] + + + 22 -> 25 ; +21 [label="21: Exit A_add \n " color=yellow style=filled] + + +20 [label="20: Start A_add\nFormals: this:struct A other:struct A &\nLocals: \n DECLARE_LOCALS(&return); [line 41]\n NULLIFY(&this,false); [line 41]\n NULLIFY(&other,false); [line 41]\n " color=yellow style=filled] + + + 20 -> 21 ; +19 [label="19: Return Stmt \n n$8=*&a:int [line 38]\n n$9=*&b:int [line 38]\n *&return:int =(n$8 + n$9) [line 38]\n REMOVE_TEMPS(n$8,n$9); [line 38]\n NULLIFY(&a,false); [line 38]\n NULLIFY(&b,false); [line 38]\n APPLY_ABSTRACTION; [line 38]\n " shape="box"] + + + 19 -> 18 ; +18 [label="18: Exit A::AIn_fun \n " color=yellow style=filled] + + +17 [label="17: Start A::AIn_fun\nFormals: this:struct A::AIn a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 37]\n NULLIFY(&this,false); [line 37]\n " color=yellow style=filled] + + + 17 -> 19 ; +16 [label="16: DeclStmt \n n$6=*&a:int [line 31]\n n$7=*&b:int [line 31]\n *&c:int =((n$6 + n$7) + 1) [line 31]\n REMOVE_TEMPS(n$6,n$7); [line 31]\n NULLIFY(&a,false); [line 31]\n NULLIFY(&b,false); [line 31]\n " shape="box"] 16 -> 15 ; -15 [label="15: Exit A::AIn_fun \n " color=yellow style=filled] +15 [label="15: Return Stmt \n n$4=*&c:int [line 34]\n n$5=*&c:int [line 34]\n *&return:int =(n$4 * n$5) [line 34]\n REMOVE_TEMPS(n$4,n$5); [line 34]\n NULLIFY(&c,false); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] -14 [label="14: Start A::AIn_fun\nFormals: this:struct A::AIn a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 35]\n NULLIFY(&this,false); [line 35]\n " color=yellow style=filled] + 15 -> 14 ; +14 [label="14: Exit A_fun \n " color=yellow style=filled] - 14 -> 16 ; -13 [label="13: DeclStmt \n n$3=*&a:int [line 29]\n n$4=*&b:int [line 29]\n *&c:int =((n$3 + n$4) + 1) [line 29]\n REMOVE_TEMPS(n$3,n$4); [line 29]\n NULLIFY(&a,false); [line 29]\n NULLIFY(&b,false); [line 29]\n " shape="box"] +13 [label="13: Start A_fun\nFormals: this:struct A a:int b:int \nLocals: c:int \n DECLARE_LOCALS(&return,&c); [line 30]\n NULLIFY(&this,false); [line 30]\n NULLIFY(&c,false); [line 30]\n " color=yellow style=filled] - 13 -> 12 ; -12 [label="12: Return Stmt \n n$1=*&c:int [line 32]\n n$2=*&c:int [line 32]\n *&return:int =(n$1 * n$2) [line 32]\n REMOVE_TEMPS(n$1,n$2); [line 32]\n NULLIFY(&c,false); [line 32]\n APPLY_ABSTRACTION; [line 32]\n " shape="box"] + 13 -> 16 ; +12 [label="12: Exit A_fun \n " color=yellow style=filled] - 12 -> 11 ; -11 [label="11: Exit A_fun \n " color=yellow style=filled] +11 [label="11: Start A_fun\nFormals: this:struct A a:int b:int c:int \nLocals: \n DECLARE_LOCALS(&return); [line 25]\n NULLIFY(&this,false); [line 25]\n NULLIFY(&a,false); [line 25]\n NULLIFY(&b,false); [line 25]\n NULLIFY(&c,false); [line 25]\n " color=yellow style=filled] -10 [label="10: Start A_fun\nFormals: this:struct A a:int b:int \nLocals: c:int \n DECLARE_LOCALS(&return,&c); [line 28]\n NULLIFY(&this,false); [line 28]\n NULLIFY(&c,false); [line 28]\n " color=yellow style=filled] + 11 -> 12 ; +10 [label="10: Return Stmt \n n$1=*&a:int [line 20]\n n$2=*&b:int [line 20]\n n$3=*&c:int [line 20]\n *&return:int =((n$1 + n$2) + n$3) [line 20]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 20]\n NULLIFY(&a,false); [line 20]\n NULLIFY(&b,false); [line 20]\n NULLIFY(&c,false); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] - 10 -> 13 ; -9 [label="9: Exit A_fun \n " color=yellow style=filled] + 10 -> 9 ; +9 [label="9: Exit A_fun_default \n " color=yellow style=filled] -8 [label="8: Start A_fun\nFormals: this:struct A a:int b:int c:int \nLocals: \n DECLARE_LOCALS(&return); [line 23]\n NULLIFY(&this,false); [line 23]\n NULLIFY(&a,false); [line 23]\n NULLIFY(&b,false); [line 23]\n NULLIFY(&c,false); [line 23]\n " color=yellow style=filled] +8 [label="8: Start A_fun_default\nFormals: this:struct A a:int b:int c:int \nLocals: \n DECLARE_LOCALS(&return); [line 20]\n NULLIFY(&this,false); [line 20]\n " color=yellow style=filled] - 8 -> 9 ; + 8 -> 10 ; 7 [label="7: DeclStmt \n *&c:int =10 [line 17]\n " shape="box"]