From bd935c2347a280ef7313b15111f34a875b31147e Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Fri, 8 Jan 2016 08:48:32 -0800 Subject: [PATCH] Add destructor calls on delete expression Summary: public Add destructor calls on delete expression. While not the most important, it is the simplest case of adding destructor calls. This will help us in the future with more complex cases. Reviewed By: ddino Differential Revision: D2773483 fb-gh-sync-id: 4df9c73 --- infer/src/clang/cTrans.ml | 36 ++++++++++++++++--- .../frontend/destructors/call_on_delete.cpp | 20 +++++++++++ .../destructors/call_on_delete.cpp.dot | 31 ++++++++++++++++ infer/tests/frontend/cpp/DestructorsTest.java | 6 ++++ 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp create mode 100644 infer/tests/codetoanalyze/cpp/frontend/destructors/call_on_delete.cpp.dot 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"); + } + }