diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index e124f2b7c..4d710369e 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -421,6 +421,18 @@ struct Cfg.set_procname_priority context.CContext.cfg pname; { pre_trans_result with exps = [method_exp] @ extra_exps } + let destructor_deref_trans trans_state pvar_trans_result class_type_ptr = + let open Clang_ast_t in + let destruct_decl_ref_opt = match Ast_utils.get_decl_from_typ_ptr class_type_ptr with + | Some CXXRecordDecl (_, _, _ , _, _, _, _, cxx_record_info) + | Some ClassTemplateSpecializationDecl (_, _, _, _, _, _, _, cxx_record_info) -> + cxx_record_info.xrdi_destructor + | _ -> None in + match destruct_decl_ref_opt with + | Some decl_ref -> method_deref_trans trans_state pvar_trans_result decl_ref + | None -> empty_res_trans + + let cxxThisExpr_trans trans_state stmt_info expr_info = let context = trans_state.context in let sil_loc = CLocation.get_sil_location stmt_info context in @@ -774,6 +786,13 @@ struct Sil.Tvoid | _ -> assert false + and cxx_destructor_call_trans trans_state si this_res_trans class_type_ptr = + let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in + let res_trans_callee = destructor_deref_trans trans_state this_res_trans class_type_ptr in + if res_trans_callee.exps <> [] then + cxx_method_construct_call_trans trans_state_pri res_trans_callee [] si Sil.Tvoid + else empty_res_trans + and objCMessageExpr_trans_special_cases trans_state si obj_c_message_expr_info stmt_list expr_info method_type trans_state_pri sil_loc act_params = let context = trans_state.context in @@ -1759,7 +1778,7 @@ struct (* 1. Handle __new_array *) (* 2. Handle initialization values *) - and cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info = + and cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info delete_expr_info = let context = trans_state.context in let sil_loc = CLocation.get_sil_location stmt_info context in let fname = SymExec.ModelBuiltins.__delete in @@ -1769,10 +1788,19 @@ struct let result_trans_param = exec_with_self_exception instruction trans_state_param param in let exp = extract_exp_from_list result_trans_param.exps "WARNING: There should be one expression to delete. \n" in + let deleted_type = delete_expr_info.Clang_ast_t.xdei_destroyed_type in + (* create stmt_info with new pointer so that destructor call doesn't create a node *) + let destruct_stmt_info = { stmt_info with + Clang_ast_t.si_pointer = Ast_utils.get_fresh_pointer () } in + (* use empty_res_trans to avoid ending up with same instruction twice *) + (* otherwise it would happen due to structutre of all_res_trans *) + let this_res_trans_destruct = { empty_res_trans with exps = result_trans_param.exps } in + let destruct_res_trans = cxx_destructor_call_trans trans_state_pri destruct_stmt_info + this_res_trans_destruct deleted_type in (* function is void *) let call_instr = Sil.Call ([], (Sil.Const (Sil.Cfun fname)), [exp], sil_loc, Sil.cf_default) in let call_res_trans = { empty_res_trans with instrs = [call_instr] } in - let all_res_trans = [ result_trans_param; call_res_trans] in + let all_res_trans = [ result_trans_param; destruct_res_trans; call_res_trans] in let res_trans = PriorityNode.compute_results_to_parent trans_state_pri sil_loc "Call delete" stmt_info all_res_trans in { res_trans with exps = [] } @@ -2005,8 +2033,8 @@ struct assert false) | CXXNewExpr (stmt_info, stmt_list, expr_info, _) -> cxxNewExpr_trans trans_state stmt_info expr_info - | CXXDeleteExpr (stmt_info, stmt_list, expr_info, _) -> - cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info + | CXXDeleteExpr (stmt_info, stmt_list, expr_info, delete_expr_info) -> + cxxDeleteExpr_trans trans_state stmt_info stmt_list expr_info delete_expr_info | MaterializeTemporaryExpr (stmt_info, stmt_list, expr_info, _) -> materializeTemporaryExpr_trans trans_state stmt_info stmt_list expr_info | s -> (Printing.log_stats diff --git a/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp b/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp new file mode 100644 index 000000000..8aff42e99 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015 - 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 { + ~X() {} +}; + +void deleteX(X *x) { + delete x; +} + +void deleteInt(int *x) { + delete x; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp.dot new file mode 100644 index 000000000..228e06d85 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp.dot @@ -0,0 +1,31 @@ +digraph iCFG { +8 [label="8: Call delete \n n$0=*&x:int * [line 19]\n _fun___delete(n$0:int *) [line 19]\n REMOVE_TEMPS(n$0); [line 19]\n NULLIFY(&x,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Exit deleteInt \n " color=yellow style=filled] + + +6 [label="6: Start deleteInt\nFormals: x:int *\nLocals: \n DECLARE_LOCALS(&return); [line 18]\n " color=yellow style=filled] + + + 6 -> 8 ; +5 [label="5: Call delete \n n$0=*&x:class X * [line 15]\n _fun_X_~X(n$0:class X *) [line 15]\n _fun___delete(n$0:class X *) [line 15]\n REMOVE_TEMPS(n$0); [line 15]\n NULLIFY(&x,false); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: Exit deleteX \n " color=yellow style=filled] + + +3 [label="3: Start deleteX\nFormals: x:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 14]\n " color=yellow style=filled] + + + 3 -> 5 ; +2 [label="2: Exit X_~X \n " color=yellow style=filled] + + +1 [label="1: Start X_~X\nFormals: this:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 11]\n NULLIFY(&this,false); [line 11]\n " color=yellow style=filled] + + + 1 -> 2 ; +} diff --git a/infer/tests/frontend/cpp/DestructorsTest.java b/infer/tests/frontend/cpp/DestructorsTest.java index bb478070d..96ed2f426 100644 --- a/infer/tests/frontend/cpp/DestructorsTest.java +++ b/infer/tests/frontend/cpp/DestructorsTest.java @@ -35,4 +35,10 @@ public class DestructorsTest { frontendTest("simple_decl.cpp"); } + @Test + public void testCallOnDeleteDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("call_on_delete.cpp"); + } + }