From bdab22a093b3b4c5261f5295bb3bd9eefa8aec17 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Fri, 29 Jan 2016 07:16:29 -0800 Subject: [PATCH] Support temporary objects (CXXTemporaryObjectExpr) Summary: public 1. Add support for temporary C++ objects. 2. Make constructor calls return constructed objects - it allows us pass them as parameters to another constructs (such as parameters, member expressions etc.) 3. Translate FunctionalCastExpr which sometimes is used instead of CXXTemporaryObjectExpr Reviewed By: dulmarod Differential Revision: D2874916 fb-gh-sync-id: d9ac2cc --- infer/src/clang/cTrans.ml | 24 ++- infer/src/clang/cTrans_utils.ml | 3 +- .../cpp/frontend/constructors/temp_object.cpp | 57 +++++++ .../frontend/constructors/temp_object.cpp.dot | 160 ++++++++++++++++++ .../cpp/frontend/types/casts.cpp | 4 + .../cpp/frontend/types/casts.cpp.dot | 11 ++ infer/tests/endtoend/cpp/TempObjectTest.java | 68 ++++++++ .../tests/frontend/cpp/ConstructorsTest.java | 6 + 8 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp create mode 100644 infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp.dot create mode 100644 infer/tests/endtoend/cpp/TempObjectTest.java diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 9adbc70d2..139a8bf9e 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -857,21 +857,30 @@ struct and cxxConstructExpr_trans trans_state expr = match expr with - | Clang_ast_t.CXXConstructExpr (si, params_stmt, ei, cxx_constr_info) -> + | Clang_ast_t.CXXConstructExpr (si, params_stmt, ei, cxx_constr_info) + | Clang_ast_t.CXXTemporaryObjectExpr (si, params_stmt, ei, cxx_constr_info) -> let context = trans_state.context in let trans_state_pri = PriorityNode.try_claim_priority_node trans_state si in let decl_ref = cxx_constr_info.Clang_ast_t.xcei_decl_ref in let class_type = CTypes_decl.get_type_from_expr_info ei context.CContext.tenv in let this_type = Sil.Tptr (class_type, Sil.Pk_pointer) in - let var_exp = match trans_state.var_exp with Some e -> e | None -> assert false in + let var_exp = match trans_state.var_exp with + | Some e -> e + | None -> + let tenv = trans_state.context.CContext.tenv in + let procdesc = trans_state.context.CContext.procdesc in + let pvar = mk_temp_sil_var tenv procdesc "__temp_construct_" in + Cfg.Procdesc.append_locals procdesc [(Sil.pvar_get_name pvar, class_type)]; + Sil.Lvar pvar in let this_res_trans = { empty_res_trans with exps = [(var_exp, this_type)]; initd_exps = [var_exp]; } in let res_trans_callee = decl_ref_trans trans_state this_res_trans si ei decl_ref in let params_stmt' = assign_default_params params_stmt expr in - cxx_method_construct_call_trans trans_state_pri res_trans_callee params_stmt' si - Sil.Tvoid + let res_trans = cxx_method_construct_call_trans trans_state_pri res_trans_callee + params_stmt' si Sil.Tvoid in + { res_trans with exps = [(var_exp, class_type)] } | _ -> assert false and cxx_destructor_call_trans trans_state si this_res_trans class_type_ptr = @@ -1967,7 +1976,7 @@ struct | CXXOperatorCallExpr(stmt_info, stmt_list, ei) -> callExpr_trans trans_state stmt_info stmt_list ei - | CXXConstructExpr _ -> + | CXXConstructExpr _ | CXXTemporaryObjectExpr _ -> cxxConstructExpr_trans trans_state instr | ObjCMessageExpr(stmt_info, stmt_list, expr_info, obj_c_message_expr_info) -> @@ -2039,7 +2048,8 @@ struct cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind true | ImplicitCastExpr(stmt_info, stmt_list, expr_info, cast_kind) | CStyleCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _) - | CXXStaticCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _) -> + | CXXStaticCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _) + | CXXFunctionalCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _)-> cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind false | IntegerLiteral(stmt_info, _, expr_info, integer_literal_info) -> @@ -2154,7 +2164,7 @@ struct compoundLiteralExpr_trans trans_state stmt_info stmt_list expr_info | InitListExpr (stmt_info, stmts, expr_info) -> initListExpr_trans trans_state stmt_info expr_info stmts - | CXXBindTemporaryExpr (stmt_info, stmt_list, expr_info, cxx_bind_temp_expr_info) -> + | CXXBindTemporaryExpr (stmt_info, stmt_list, expr_info, cxx_bind_temp_expr_info)-> (* right now we ignore this expression and try to translate the child node *) parenExpr_trans trans_state stmt_info stmt_list | s -> (Printing.log_stats diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 9d35ff22a..c0b4239bb 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -659,7 +659,8 @@ let rec pointer_of_call_expr stmt = | MemberExpr(_, _, _, member_expr_info) -> let decl_ref = member_expr_info.Clang_ast_t.mei_decl_ref in Some decl_ref.Clang_ast_t.dr_decl_pointer - | CXXConstructExpr (_, _, _, cxx_construct_expr_info) -> + | CXXConstructExpr (_, _, _, cxx_construct_expr_info) + | CXXTemporaryObjectExpr (_, _, _, cxx_construct_expr_info) -> let decl_ref = cxx_construct_expr_info.xcei_decl_ref in Some decl_ref.Clang_ast_t.dr_decl_pointer | _ -> diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp b/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp new file mode 100644 index 000000000..c4e0f2005 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp @@ -0,0 +1,57 @@ +/* + * 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 { + X(int a) { f = a; } + X(int a, int b) { f = a; } + // copy constructor without init list + X(const X& x) { f = x.f; } + int f; + int div() { return 1/f; } +}; + +int div(int f) { return 1 / f; } + +// passing by value doesn't work +// int divX(X x) { return 1 / x.f; } + +X getX(int a, int b) { return X(a,b); } + +int assign_temp_div0() { + X x = X(0, 1); + return x.div(); +} + +int temp_field_div0() { + return div(X(0,1).f); +} + +int temp_field2_div0() { + return div(X(0).f); +} + +int temp_method_div0() { + return X(0,1).div(); +} + +int getX_field_div0() { + return div(getX(0,1).f); +} + +int getX_method_div0() { + return getX(0,1).div(); +} + +int temp_field_div1() { + return div(X(1,0).f); +} + +int getX_field_div1() { + return div(getX(1,0).f); +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp.dot new file mode 100644 index 000000000..c95a2f859 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.cpp.dot @@ -0,0 +1,160 @@ +digraph iCFG { +43 [label="43: Return Stmt \n _fun_getX(1:int ,0:int ,&__temp_return_n$1:class X *) [line 56]\n n$2=*&__temp_return_n$1.f:int [line 56]\n n$3=_fun_div(n$2:int ) [line 56]\n *&return:int =n$3 [line 56]\n REMOVE_TEMPS(n$2,n$3); [line 56]\n NULLIFY(&__temp_return_n$1,false); [line 56]\n APPLY_ABSTRACTION; [line 56]\n " shape="box"] + + + 43 -> 42 ; +42 [label="42: Exit getX_field_div1 \n " color=yellow style=filled] + + +41 [label="41: Start getX_field_div1\nFormals: \nLocals: __temp_return_n$1:class X \n DECLARE_LOCALS(&return,&__temp_return_n$1); [line 55]\n " color=yellow style=filled] + + + 41 -> 43 ; +40 [label="40: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,1:int ,0:int ) [line 52]\n n$1=*&__temp_construct_n$0.f:int [line 52]\n n$2=_fun_div(n$1:int ) [line 52]\n *&return:int =n$2 [line 52]\n REMOVE_TEMPS(n$1,n$2); [line 52]\n NULLIFY(&__temp_construct_n$0,false); [line 52]\n APPLY_ABSTRACTION; [line 52]\n " shape="box"] + + + 40 -> 39 ; +39 [label="39: Exit temp_field_div1 \n " color=yellow style=filled] + + +38 [label="38: Start temp_field_div1\nFormals: \nLocals: __temp_construct_n$0:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0); [line 51]\n " color=yellow style=filled] + + + 38 -> 40 ; +37 [label="37: Return Stmt \n _fun_getX(0:int ,1:int ,&__temp_return_n$1:class X *) [line 48]\n n$2=_fun_X_div(&__temp_return_n$1:class X &) [line 48]\n *&return:int =n$2 [line 48]\n REMOVE_TEMPS(n$2); [line 48]\n NULLIFY(&__temp_return_n$1,false); [line 48]\n APPLY_ABSTRACTION; [line 48]\n " shape="box"] + + + 37 -> 36 ; +36 [label="36: Exit getX_method_div0 \n " color=yellow style=filled] + + +35 [label="35: Start getX_method_div0\nFormals: \nLocals: __temp_return_n$1:class X \n DECLARE_LOCALS(&return,&__temp_return_n$1); [line 47]\n " color=yellow style=filled] + + + 35 -> 37 ; +34 [label="34: Return Stmt \n _fun_getX(0:int ,1:int ,&__temp_return_n$1:class X *) [line 44]\n n$2=*&__temp_return_n$1.f:int [line 44]\n n$3=_fun_div(n$2:int ) [line 44]\n *&return:int =n$3 [line 44]\n REMOVE_TEMPS(n$2,n$3); [line 44]\n NULLIFY(&__temp_return_n$1,false); [line 44]\n APPLY_ABSTRACTION; [line 44]\n " shape="box"] + + + 34 -> 33 ; +33 [label="33: Exit getX_field_div0 \n " color=yellow style=filled] + + +32 [label="32: Start getX_field_div0\nFormals: \nLocals: __temp_return_n$1:class X \n DECLARE_LOCALS(&return,&__temp_return_n$1); [line 43]\n " color=yellow style=filled] + + + 32 -> 34 ; +31 [label="31: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,0:int ,1:int ) [line 40]\n n$1=_fun_X_div(&__temp_construct_n$0:class X &) [line 40]\n *&return:int =n$1 [line 40]\n REMOVE_TEMPS(n$1); [line 40]\n NULLIFY(&__temp_construct_n$0,false); [line 40]\n APPLY_ABSTRACTION; [line 40]\n " shape="box"] + + + 31 -> 30 ; +30 [label="30: Exit temp_method_div0 \n " color=yellow style=filled] + + +29 [label="29: Start temp_method_div0\nFormals: \nLocals: __temp_construct_n$0:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0); [line 39]\n " color=yellow style=filled] + + + 29 -> 31 ; +28 [label="28: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,0:int ) [line 36]\n n$1=*&__temp_construct_n$0.f:int [line 36]\n n$2=_fun_div(n$1:int ) [line 36]\n *&return:int =n$2 [line 36]\n REMOVE_TEMPS(n$1,n$2); [line 36]\n NULLIFY(&__temp_construct_n$0,false); [line 36]\n APPLY_ABSTRACTION; [line 36]\n " shape="box"] + + + 28 -> 27 ; +27 [label="27: Exit temp_field2_div0 \n " color=yellow style=filled] + + +26 [label="26: Start temp_field2_div0\nFormals: \nLocals: __temp_construct_n$0:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0); [line 35]\n " color=yellow style=filled] + + + 26 -> 28 ; +25 [label="25: Return Stmt \n _fun_X_X(&__temp_construct_n$0:class X *,0:int ,1:int ) [line 32]\n n$1=*&__temp_construct_n$0.f:int [line 32]\n n$2=_fun_div(n$1:int ) [line 32]\n *&return:int =n$2 [line 32]\n REMOVE_TEMPS(n$1,n$2); [line 32]\n NULLIFY(&__temp_construct_n$0,false); [line 32]\n APPLY_ABSTRACTION; [line 32]\n " shape="box"] + + + 25 -> 24 ; +24 [label="24: Exit temp_field_div0 \n " color=yellow style=filled] + + +23 [label="23: Start temp_field_div0\nFormals: \nLocals: __temp_construct_n$0:class X \n DECLARE_LOCALS(&return,&__temp_construct_n$0); [line 31]\n " color=yellow style=filled] + + + 23 -> 25 ; +22 [label="22: DeclStmt \n _fun_X_X(&SIL_materialize_temp__n$1:class X *,0:int ,1:int ) [line 27]\n _fun_X_X(&x:class X *,&SIL_materialize_temp__n$1:class X &) [line 27]\n " shape="box"] + + + 22 -> 21 ; +21 [label="21: Return Stmt \n n$0=_fun_X_div(&x:class X &) [line 28]\n *&return:int =n$0 [line 28]\n REMOVE_TEMPS(n$0); [line 28]\n NULLIFY(&SIL_materialize_temp__n$1,false); [line 28]\n NULLIFY(&x,false); [line 28]\n APPLY_ABSTRACTION; [line 28]\n " shape="box"] + + + 21 -> 20 ; +20 [label="20: Exit assign_temp_div0 \n " color=yellow style=filled] + + +19 [label="19: Start assign_temp_div0\nFormals: \nLocals: x:class X SIL_materialize_temp__n$1:class X \n DECLARE_LOCALS(&return,&x,&SIL_materialize_temp__n$1); [line 26]\n " color=yellow style=filled] + + + 19 -> 22 ; +18 [label="18: Return Stmt \n n$0=*&__return_param:void * [line 24]\n n$2=*&a:int [line 24]\n n$3=*&b:int [line 24]\n _fun_X_X(&SIL_materialize_temp__n$1:class X *,n$2:int ,n$3:int ) [line 24]\n _fun_X_X(n$0:class X *,&SIL_materialize_temp__n$1:class X &) [line 24]\n REMOVE_TEMPS(n$0,n$2,n$3); [line 24]\n NULLIFY(&__return_param,false); [line 24]\n NULLIFY(&a,false); [line 24]\n NULLIFY(&b,false); [line 24]\n NULLIFY(&SIL_materialize_temp__n$1,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"] + + + 18 -> 17 ; +17 [label="17: Exit getX \n " color=yellow style=filled] + + +16 [label="16: Start getX\nFormals: a:int b:int __return_param:class X *\nLocals: SIL_materialize_temp__n$1:class X \n DECLARE_LOCALS(&return,&SIL_materialize_temp__n$1); [line 24]\n " color=yellow style=filled] + + + 16 -> 18 ; +15 [label="15: Return Stmt \n n$0=*&f:int [line 19]\n *&return:int =(1 / n$0) [line 19]\n REMOVE_TEMPS(n$0); [line 19]\n NULLIFY(&f,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit div \n " color=yellow style=filled] + + +13 [label="13: Start div\nFormals: f:int \nLocals: \n DECLARE_LOCALS(&return); [line 19]\n " color=yellow style=filled] + + + 13 -> 15 ; +12 [label="12: Return Stmt \n n$0=*&this:class X * [line 16]\n n$1=*n$0.f:int [line 16]\n *&return:int =(1 / n$1) [line 16]\n REMOVE_TEMPS(n$0,n$1); [line 16]\n NULLIFY(&this,false); [line 16]\n APPLY_ABSTRACTION; [line 16]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Exit X_div \n " color=yellow style=filled] + + +10 [label="10: Start X_div\nFormals: this:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 16]\n " color=yellow style=filled] + + + 10 -> 12 ; +9 [label="9: BinaryOperatorStmt: Assign \n n$0=*&this:class X * [line 14]\n n$1=*&x:class X & [line 14]\n n$2=*n$1.f:int [line 14]\n *n$0.f:int =n$2 [line 14]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 14]\n NULLIFY(&this,false); [line 14]\n NULLIFY(&x,false); [line 14]\n APPLY_ABSTRACTION; [line 14]\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 * x:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 14]\n " color=yellow style=filled] + + + 7 -> 9 ; +6 [label="6: BinaryOperatorStmt: Assign \n n$0=*&this:class X * [line 12]\n n$1=*&a:int [line 12]\n *n$0.f:int =n$1 [line 12]\n REMOVE_TEMPS(n$0,n$1); [line 12]\n NULLIFY(&a,false); [line 12]\n NULLIFY(&this,false); [line 12]\n APPLY_ABSTRACTION; [line 12]\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 * a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 12]\n NULLIFY(&b,false); [line 12]\n " color=yellow style=filled] + + + 4 -> 6 ; +3 [label="3: BinaryOperatorStmt: Assign \n n$0=*&this:class X * [line 11]\n n$1=*&a:int [line 11]\n *n$0.f:int =n$1 [line 11]\n REMOVE_TEMPS(n$0,n$1); [line 11]\n NULLIFY(&a,false); [line 11]\n NULLIFY(&this,false); [line 11]\n APPLY_ABSTRACTION; [line 11]\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 * a:int \nLocals: \n DECLARE_LOCALS(&return); [line 11]\n " color=yellow style=filled] + + + 1 -> 3 ; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp b/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp index 43ba7ba2a..e9f98b767 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp +++ b/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp @@ -11,3 +11,7 @@ void stat_cast() { int a; long long la = static_cast(a); } + +void functional_cast() { + int a = int(2 + 3.4); +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp.dot index 2148536a0..8be61c3e9 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/types/casts.cpp.dot @@ -1,4 +1,15 @@ digraph iCFG { +6 [label="6: DeclStmt \n *&a:int =(2 + 3.400000) [line 16]\n NULLIFY(&a,false); [line 16]\n APPLY_ABSTRACTION; [line 16]\n " shape="box"] + + + 6 -> 5 ; +5 [label="5: Exit functional_cast \n " color=yellow style=filled] + + +4 [label="4: Start functional_cast\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 15]\n NULLIFY(&a,false); [line 15]\n " color=yellow style=filled] + + + 4 -> 6 ; 3 [label="3: DeclStmt \n n$0=*&a:int [line 12]\n *&la:long long =n$0 [line 12]\n REMOVE_TEMPS(n$0); [line 12]\n NULLIFY(&a,false); [line 12]\n NULLIFY(&la,false); [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] diff --git a/infer/tests/endtoend/cpp/TempObjectTest.java b/infer/tests/endtoend/cpp/TempObjectTest.java new file mode 100644 index 000000000..f5ba45864 --- /dev/null +++ b/infer/tests/endtoend/cpp/TempObjectTest.java @@ -0,0 +1,68 @@ +/* + * 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 TempObjectTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/constructors/temp_object.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 = { + "assign_temp_div0", + "temp_field_div0", + "temp_field2_div0", + "temp_method_div0", + "getX_field_div0", + "getX_method_div0", + }; + assertThat( + "Results should contain the expected divide by zero", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/frontend/cpp/ConstructorsTest.java b/infer/tests/frontend/cpp/ConstructorsTest.java index 3f0a6702a..922dcbc41 100644 --- a/infer/tests/frontend/cpp/ConstructorsTest.java +++ b/infer/tests/frontend/cpp/ConstructorsTest.java @@ -40,4 +40,10 @@ public class ConstructorsTest { throws InterruptedException, IOException, InferException { frontendTest("constructor_default_arg.cpp"); } + + @Test + public void testTempObjectDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("temp_object.cpp"); + } }