diff --git a/facebook-clang-plugins b/facebook-clang-plugins index d07910830..60b10ef73 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit d07910830a3ccf56c10a4eaf9c8dcad23ecb4d9b +Subproject commit 60b10ef73606e814cf87c08e5c0d263dc1f9fb8c diff --git a/infer/src/clang/cMethod_signature.ml b/infer/src/clang/cMethod_signature.ml index 5187df4ea..e4b0c285c 100644 --- a/infer/src/clang/cMethod_signature.ml +++ b/infer/src/clang/cMethod_signature.ml @@ -17,6 +17,7 @@ type method_signature = { attributes : Clang_ast_t.attribute list; loc : Clang_ast_t.source_range; is_instance : bool; + is_cpp_virtual : bool; language : CFrontend_config.lang; pointer_to_parent : Clang_ast_t.pointer option; pointer_to_property_opt : Clang_ast_t.pointer option; (* If set then method is a getter/setter *) @@ -44,6 +45,9 @@ let ms_get_loc { loc } = let ms_is_instance { is_instance } = is_instance +let ms_is_cpp_virtual { is_cpp_virtual } = + is_cpp_virtual + let ms_get_lang { language } = language @@ -68,8 +72,11 @@ let ms_is_setter { pointer_to_property_opt; args } = Option.is_some pointer_to_property_opt && IList.length args == 2 -let make_ms name args ret_type attributes loc is_instance language pointer_to_parent +let make_ms name args ret_type attributes loc is_instance ?is_cpp_virtual language pointer_to_parent pointer_to_property_opt return_param_typ = + let is_cpp_virtual = match is_cpp_virtual with + | Some is_cpp_virtual -> is_cpp_virtual + | None -> false in { name; args; @@ -77,6 +84,7 @@ let make_ms name args ret_type attributes loc is_instance language pointer_to_pa attributes; loc; is_instance; + is_cpp_virtual; language; pointer_to_parent; pointer_to_property_opt; diff --git a/infer/src/clang/cMethod_signature.mli b/infer/src/clang/cMethod_signature.mli index 91978cf31..6c49e4c6b 100644 --- a/infer/src/clang/cMethod_signature.mli +++ b/infer/src/clang/cMethod_signature.mli @@ -27,6 +27,8 @@ val ms_get_loc : method_signature -> Clang_ast_t.source_range val ms_is_instance : method_signature -> bool +val ms_is_cpp_virtual : method_signature -> bool + val ms_get_lang : method_signature -> CFrontend_config.lang val ms_get_pointer_to_parent : method_signature -> Clang_ast_t.pointer option @@ -40,9 +42,9 @@ val ms_is_getter : method_signature -> bool val ms_is_setter : method_signature -> bool val make_ms : Procname.t -> (string * Clang_ast_t.type_ptr) list -> Clang_ast_t.type_ptr - -> Clang_ast_t.attribute list -> Clang_ast_t.source_range -> bool -> CFrontend_config.lang - -> Clang_ast_t.pointer option -> Clang_ast_t.pointer option -> Sil.typ option - -> method_signature + -> Clang_ast_t.attribute list -> Clang_ast_t.source_range -> bool -> ?is_cpp_virtual:bool + -> CFrontend_config.lang -> Clang_ast_t.pointer option -> Clang_ast_t.pointer option + -> Sil.typ option -> method_signature val replace_name_ms : method_signature -> Procname.t -> method_signature diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index e7d6a0888..ee520562b 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -94,6 +94,11 @@ let get_language function_method_decl_info = | ObjC_Meth_decl_info _ -> CFrontend_config.OBJC | Block_decl_info _ -> CFrontend_config.OBJC +let is_cpp_virtual function_method_decl_info = + match function_method_decl_info with + | Cpp_Meth_decl_info (_, mdi, _, _) -> mdi.Clang_ast_t.xmdi_is_virtual + | _ -> false + (** Returns parameters of a function/method. They will have following order: 1. self/this parameter (optional, only for methods) 2. normal parameters @@ -125,9 +130,10 @@ let build_method_signature tenv decl_info procname function_method_decl_info let parameters = get_parameters tenv function_method_decl_info in let attributes = decl_info.Clang_ast_t.di_attributes in let lang = get_language function_method_decl_info in + let is_cpp_virtual = is_cpp_virtual function_method_decl_info in CMethod_signature.make_ms - procname parameters tp attributes source_range is_instance_method lang parent_pointer - pointer_to_property_opt return_param_type_opt + procname parameters tp attributes source_range is_instance_method ~is_cpp_virtual:is_cpp_virtual + lang parent_pointer pointer_to_property_opt return_param_type_opt let get_assume_not_null_calls param_decls = let do_one_param decl = match decl with diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 3a00660b4..bbf97061c 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -493,10 +493,13 @@ struct let class_name = Ast_utils.get_class_name_from_member name_info in Printing.log_out "!!!!! Dealing with method '%s' @." method_name; let method_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in - let is_instance_method = - match CMethod_trans.method_signature_of_pointer context.tenv decl_ptr with + let ms_opt = CMethod_trans.method_signature_of_pointer context.tenv decl_ptr in + let is_instance_method = match ms_opt with | Some ms -> CMethod_signature.ms_is_instance ms | _ -> true in (* might happen for methods that are not exported yet (some templates). *) + let is_cpp_virtual = match ms_opt with + | Some ms -> CMethod_signature.ms_is_cpp_virtual ms + | _ -> false in let extra_exps = if is_instance_method then ( (* pre_trans_result.exps may contain expr for 'this' parameter:*) (* if it comes from CXXMemberCallExpr it will be there *) @@ -518,7 +521,8 @@ struct method_name type_ptr in let method_exp = (Sil.Const (Sil.Cfun pname), method_typ) in Cfg.set_procname_priority context.CContext.cfg pname; - { pre_trans_result with exps = [method_exp] @ extra_exps } + { pre_trans_result with + exps = [method_exp] @ extra_exps; is_cpp_call_virtual = is_cpp_virtual } let destructor_deref_trans trans_state pvar_trans_result class_type_ptr = let open Clang_ast_t in @@ -659,7 +663,8 @@ struct ids = res_trans_idx.ids @ res_trans_a.ids; instrs = res_trans_a.instrs @ res_trans_idx.instrs; exps = [(array_exp, typ)]; - initd_exps = res_trans_idx.initd_exps @ res_trans_a.initd_exps; } + initd_exps = res_trans_idx.initd_exps @ res_trans_a.initd_exps; + is_cpp_call_virtual = false;} and binaryOperator_trans trans_state binary_operator_info stmt_info expr_info stmt_list = let open Clang_ast_t in @@ -815,7 +820,7 @@ struct { res_trans_to_parent with exps = res_trans_call.exps } and cxx_method_construct_call_trans trans_state_pri result_trans_callee params_stmt - si function_type = + si function_type is_cpp_call_virtual = let open CContext in let context = trans_state_pri.context in let procname = Cfg.Procdesc.get_proc_name context.procdesc in @@ -838,7 +843,7 @@ struct (* first expr is method address, rest are params including 'this' parameter *) let actual_params = IList.tl (collect_exprs result_trans_subexprs) in let call_flags = { - Sil.cf_virtual = false (* TODO t7725350 *); + Sil.cf_virtual = is_cpp_call_virtual; Sil.cf_interface = false; Sil.cf_noreturn = false; Sil.cf_is_objc_block = false; @@ -867,10 +872,11 @@ struct (* claim priority if no ancestors has claimed priority before *) let trans_state_callee = { trans_state_pri with succ_nodes = [] } in let result_trans_callee = instruction trans_state_callee fun_exp_stmt in + let is_cpp_call_virtual = result_trans_callee.is_cpp_call_virtual in let fn_type_no_ref = CTypes_decl.get_type_from_expr_info expr_info context.CContext.tenv in let function_type = add_reference_if_glvalue fn_type_no_ref expr_info in cxx_method_construct_call_trans trans_state_pri result_trans_callee params_stmt - si function_type + si function_type is_cpp_call_virtual and cxxConstructExpr_trans trans_state si params_stmt ei cxx_constr_info = let context = trans_state.context in @@ -891,14 +897,16 @@ struct } in let res_trans_callee = decl_ref_trans trans_state this_res_trans si decl_ref in let res_trans = cxx_method_construct_call_trans trans_state_pri res_trans_callee - params_stmt si Sil.Tvoid in + params_stmt si Sil.Tvoid false in { res_trans with exps = [(var_exp, class_type)] } 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 + let is_cpp_call_virtual = res_trans_callee.is_cpp_call_virtual in if res_trans_callee.exps <> [] then cxx_method_construct_call_trans trans_state_pri res_trans_callee [] si Sil.Tvoid + is_cpp_call_virtual else empty_res_trans and objCMessageExpr_trans_special_cases trans_state si obj_c_message_expr_info @@ -1073,6 +1081,7 @@ struct instrs = instrs; exps = [(Sil.Var id, typ)]; initd_exps = []; (* TODO we should get exps from branches+cond *) + is_cpp_call_virtual = false; } | _ -> assert false) @@ -1112,6 +1121,7 @@ struct instrs = instrs'; exps = e'; initd_exps = []; + is_cpp_call_virtual = false; } in (* This function translate (s1 binop s2) doing shortcircuit for '&&' and '||' *) @@ -1145,6 +1155,7 @@ struct instrs = res_trans_s1.instrs@res_trans_s2.instrs; exps = [(e_cond, typ1)]; initd_exps = []; + is_cpp_call_virtual = false; } in Printing.log_out "Translating Condition for Conditional/Loop \n"; let open Clang_ast_t in @@ -1204,6 +1215,7 @@ struct instrs = []; exps = []; initd_exps = []; + is_cpp_call_virtual = false; } | _ -> assert false) @@ -1439,11 +1451,11 @@ struct (* Iteration over colection - + for (v : C) { body; } - - is translated as follows: - + + is translated as follows: + TypeC __range = C; for (__begin = __range.begin(), __end = __range.end(); __begin != __end; @@ -1457,10 +1469,10 @@ struct let open Clang_ast_t in match stmt_list with | [iterator_decl; initial_cond; exit_cond; increment; assign_current_index; loop_body] -> - let loop_body' = CompoundStmt (stmt_info, [assign_current_index; loop_body]) in - let null_stmt = NullStmt (stmt_info, []) in - let for_loop = ForStmt (stmt_info, [initial_cond; null_stmt; exit_cond; increment; loop_body']) in - instruction trans_state (CompoundStmt (stmt_info, [iterator_decl; for_loop])) + let loop_body' = CompoundStmt (stmt_info, [assign_current_index; loop_body]) in + let null_stmt = NullStmt (stmt_info, []) in + let for_loop = ForStmt (stmt_info, [initial_cond; null_stmt; exit_cond; increment; loop_body']) in + instruction trans_state (CompoundStmt (stmt_info, [iterator_decl; for_loop])) | _ -> assert false (* Fast iteration for colection @@ -1567,6 +1579,7 @@ struct instrs = instructions; exps = [(var_exp, var_type)]; initd_exps = [var_exp]; + is_cpp_call_virtual = false; } ) else { root_nodes = []; @@ -1575,6 +1588,7 @@ struct instrs = instructions; exps = [(var_exp, var_type)]; initd_exps = [var_exp]; + is_cpp_call_virtual = false; } ) @@ -1652,7 +1666,9 @@ struct ids = res_trans_tmp.ids @ res_trans_vd.ids; instrs = res_trans_tmp.instrs @ res_trans_vd.instrs; exps = res_trans_tmp.exps @ res_trans_vd.exps; - initd_exps = res_trans_tmp.initd_exps @ res_trans_vd.initd_exps; } + initd_exps = res_trans_tmp.initd_exps @ res_trans_vd.initd_exps; + is_cpp_call_virtual = false; + } | CXXRecordDecl _ :: var_decls' (*C++/C record decl treated in the same way *) | RecordDecl _ :: var_decls' -> (* Record declaration is done in the beginning when procdesc is defined.*) @@ -1755,7 +1771,9 @@ struct and memberExpr_trans trans_state stmt_info stmt_list member_expr_info = let decl_ref = member_expr_info.Clang_ast_t.mei_decl_ref in - do_memb_ivar_ref_exp trans_state stmt_info stmt_list decl_ref + let res_trans = do_memb_ivar_ref_exp trans_state stmt_info stmt_list decl_ref in + let is_virtual_dispatch = member_expr_info.Clang_ast_t.mei_performs_virtual_dispatch in + { res_trans with is_cpp_call_virtual = res_trans.is_cpp_call_virtual && is_virtual_dispatch } and unaryOperator_trans trans_state stmt_info expr_info stmt_list unary_operator_info = let context = trans_state.context in @@ -2094,7 +2112,7 @@ struct | DoStmt(stmt_info, [body; cond]) -> doStmt_trans trans_state stmt_info cond body - + | CXXForRangeStmt (stmt_info, stmt_list) -> cxxForRangeStmt_trans trans_state stmt_info stmt_list @@ -2262,7 +2280,7 @@ struct | CXXDefaultArgExpr (_, _, _, default_arg_info) -> cxxDefaultArgExpr_trans trans_state default_arg_info - + | s -> (Printing.log_stats "\n!!!!WARNING: found statement %s. \nACTION REQUIRED: Translation need to be defined. Statement ignored.... \n" (Ast_utils.string_of_stmt s); @@ -2316,7 +2334,9 @@ struct ids = res_trans_s.ids @ res_trans_tail.ids; instrs = res_trans_tail.instrs @ res_trans_s.instrs; exps = res_trans_tail.exps @ res_trans_s.exps; - initd_exps = res_trans_tail.initd_exps @ res_trans_s.initd_exps; } in + initd_exps = res_trans_tail.initd_exps @ res_trans_s.initd_exps; + is_cpp_call_virtual = false + } in exec_trans_instrs_no_rev trans_state (IList.rev trans_stmt_fun_list) and get_clang_stmt_trans stmt = fun trans_state -> instruction trans_state stmt diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 2825a0b3d..cbb520584 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -146,6 +146,7 @@ type trans_result = { instrs: Sil.instr list; (* list of SIL instruction that need to be placed in cfg nodes of the parent*) exps: (Sil.exp * Sil.typ) list; (* SIL expressions resulting from the translation of the clang stmt *) initd_exps: Sil.exp list; + is_cpp_call_virtual : bool } (* Empty result translation *) @@ -156,6 +157,7 @@ let empty_res_trans = { instrs = []; exps = []; initd_exps = []; + is_cpp_call_virtual = false; } (** Collect the results of translating a list of instructions, and link up the nodes created. *) @@ -178,7 +180,8 @@ let collect_res_trans l = ids = rt.ids@rt'.ids; instrs = rt.instrs@rt'.instrs; exps = rt.exps@rt'.exps; - initd_exps = rt.initd_exps@rt'.initd_exps; } in + initd_exps = rt.initd_exps@rt'.initd_exps; + is_cpp_call_virtual = false} in collect l empty_res_trans (* priority_node is used to enforce some kind of policy for creating nodes *) diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index bf53e568e..2a70c1e20 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -34,6 +34,7 @@ type trans_result = { instrs: Sil.instr list; exps: (Sil.exp * Sil.typ) list; initd_exps: Sil.exp list; + is_cpp_call_virtual : bool; } val empty_res_trans: trans_result diff --git a/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp b/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp new file mode 100644 index 000000000..58580adc7 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +class Polygon { + protected: + int width, height; + + public: + virtual ~Polygon(); + void set_values(int a, int b) { + width = a; + height = b; + } + virtual int area() { return 0; } +}; + +class Rectangle : public Polygon { + public: + ~Rectangle(); + int area() { return width * height; } +}; + +class Triangle : public Polygon { + public: + ~Triangle(){}; + int area() { + int x = width * height; + return x - 10; + } +}; + +int rect_area() { + Rectangle rect; + Polygon* ppoly1 = ▭ + ppoly1->set_values(4, 5); + return 1 / (ppoly1->area() - 20); +} + +int tri_area() { + Triangle trgl; + Polygon poly; + Polygon* ppoly2 = &trgl; + ppoly2->set_values(4, 5); + return 1 / (ppoly2->area() - 10); +} + +int poly_area() { + Polygon poly; + Polygon* ppoly3 = &poly; + return 1 / ppoly3->area(); +} + +int tri_not_virtual_area() { + Triangle trgl; + Polygon poly; + Polygon* ppoly2 = &trgl; + ppoly2->set_values(4, 5); + return 1 / ppoly2->Polygon::area(); +} + +// dynamic dispatch in this case still doesn't work, +// one need special dealing with names for destructors. +// But the call gets the virtual flag. +void call_virtual_destructor() { + Polygon* trgl = new Triangle; + delete trgl; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp.dot new file mode 100644 index 000000000..8b129cc11 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.cpp.dot @@ -0,0 +1,201 @@ +digraph iCFG { +53 [label="53: DeclStmt \n n$1=_fun___new(sizeof(class Triangle ):unsigned long ) [line 71]\n *&trgl:class Triangle *=n$1 [line 71]\n REMOVE_TEMPS(n$1); [line 71]\n " shape="box"] + + + 53 -> 52 ; +52 [label="52: Call delete \n n$0=*&trgl:class Polygon * [line 72]\n _fun_Polygon_~Polygon(n$0:class Polygon *) virtual [line 72]\n _fun___delete(n$0:class Polygon *) [line 72]\n REMOVE_TEMPS(n$0); [line 72]\n NULLIFY(&trgl,false); [line 72]\n APPLY_ABSTRACTION; [line 72]\n " shape="box"] + + + 52 -> 51 ; +51 [label="51: Exit call_virtual_destructor \n " color=yellow style=filled] + + +50 [label="50: Start call_virtual_destructor\nFormals: \nLocals: trgl:class Polygon * \n DECLARE_LOCALS(&return,&trgl); [line 70]\n NULLIFY(&trgl,false); [line 70]\n " color=yellow style=filled] + + + 50 -> 53 ; +49 [label="49: DeclStmt \n _fun_Triangle_Triangle(&trgl:class Triangle *) [line 60]\n " shape="box"] + + + 49 -> 48 ; +48 [label="48: DeclStmt \n _fun_Polygon_Polygon(&poly:class Polygon *) [line 61]\n " shape="box"] + + + 48 -> 47 ; +47 [label="47: DeclStmt \n *&ppoly2:class Triangle *=&trgl [line 62]\n " shape="box"] + + + 47 -> 46 ; +46 [label="46: Call _fun_Polygon_set_values \n n$2=*&ppoly2:class Polygon * [line 63]\n _fun_Polygon_set_values(n$2:class Polygon *,4:int ,5:int ) [line 63]\n REMOVE_TEMPS(n$2); [line 63]\n " shape="box"] + + + 46 -> 45 ; +45 [label="45: Return Stmt \n n$0=*&ppoly2:class Polygon * [line 64]\n n$1=_fun_Polygon_area(n$0:class Polygon *) [line 64]\n *&return:int =(1 / n$1) [line 64]\n REMOVE_TEMPS(n$0,n$1); [line 64]\n NULLIFY(&ppoly2,false); [line 64]\n NULLIFY(&poly,false); [line 64]\n NULLIFY(&trgl,false); [line 64]\n APPLY_ABSTRACTION; [line 64]\n " shape="box"] + + + 45 -> 44 ; +44 [label="44: Exit tri_not_virtual_area \n " color=yellow style=filled] + + +43 [label="43: Start tri_not_virtual_area\nFormals: \nLocals: ppoly2:class Polygon * poly:class Polygon trgl:class Triangle \n DECLARE_LOCALS(&return,&ppoly2,&poly,&trgl); [line 59]\n NULLIFY(&ppoly2,false); [line 59]\n " color=yellow style=filled] + + + 43 -> 49 ; +42 [label="42: DeclStmt \n _fun_Polygon_Polygon(&poly:class Polygon *) [line 54]\n " shape="box"] + + + 42 -> 41 ; +41 [label="41: DeclStmt \n *&ppoly3:class Polygon *=&poly [line 55]\n " shape="box"] + + + 41 -> 40 ; +40 [label="40: Return Stmt \n n$0=*&ppoly3:class Polygon * [line 56]\n n$1=_fun_Polygon_area(n$0:class Polygon *) virtual [line 56]\n *&return:int =(1 / n$1) [line 56]\n REMOVE_TEMPS(n$0,n$1); [line 56]\n NULLIFY(&ppoly3,false); [line 56]\n NULLIFY(&poly,false); [line 56]\n APPLY_ABSTRACTION; [line 56]\n " shape="box"] + + + 40 -> 39 ; +39 [label="39: Exit poly_area \n " color=yellow style=filled] + + +38 [label="38: Start poly_area\nFormals: \nLocals: ppoly3:class Polygon * poly:class Polygon \n DECLARE_LOCALS(&return,&ppoly3,&poly); [line 53]\n NULLIFY(&ppoly3,false); [line 53]\n " color=yellow style=filled] + + + 38 -> 42 ; +37 [label="37: DeclStmt \n _fun_Triangle_Triangle(&trgl:class Triangle *) [line 46]\n " shape="box"] + + + 37 -> 36 ; +36 [label="36: DeclStmt \n _fun_Polygon_Polygon(&poly:class Polygon *) [line 47]\n " shape="box"] + + + 36 -> 35 ; +35 [label="35: DeclStmt \n *&ppoly2:class Triangle *=&trgl [line 48]\n " shape="box"] + + + 35 -> 34 ; +34 [label="34: Call _fun_Polygon_set_values \n n$2=*&ppoly2:class Polygon * [line 49]\n _fun_Polygon_set_values(n$2:class Polygon *,4:int ,5:int ) [line 49]\n REMOVE_TEMPS(n$2); [line 49]\n " shape="box"] + + + 34 -> 33 ; +33 [label="33: Return Stmt \n n$0=*&ppoly2:class Polygon * [line 50]\n n$1=_fun_Polygon_area(n$0:class Polygon *) virtual [line 50]\n *&return:int =(1 / (n$1 - 10)) [line 50]\n REMOVE_TEMPS(n$0,n$1); [line 50]\n NULLIFY(&ppoly2,false); [line 50]\n NULLIFY(&poly,false); [line 50]\n NULLIFY(&trgl,false); [line 50]\n APPLY_ABSTRACTION; [line 50]\n " shape="box"] + + + 33 -> 32 ; +32 [label="32: Exit tri_area \n " color=yellow style=filled] + + +31 [label="31: Start tri_area\nFormals: \nLocals: ppoly2:class Polygon * poly:class Polygon trgl:class Triangle \n DECLARE_LOCALS(&return,&ppoly2,&poly,&trgl); [line 45]\n NULLIFY(&ppoly2,false); [line 45]\n " color=yellow style=filled] + + + 31 -> 37 ; +30 [label="30: DeclStmt \n _fun_Rectangle_Rectangle(&rect:class Rectangle *) [line 39]\n " shape="box"] + + + 30 -> 29 ; +29 [label="29: DeclStmt \n *&ppoly1:class Rectangle *=&rect [line 40]\n " shape="box"] + + + 29 -> 28 ; +28 [label="28: Call _fun_Polygon_set_values \n n$2=*&ppoly1:class Polygon * [line 41]\n _fun_Polygon_set_values(n$2:class Polygon *,4:int ,5:int ) [line 41]\n REMOVE_TEMPS(n$2); [line 41]\n " shape="box"] + + + 28 -> 27 ; +27 [label="27: Return Stmt \n n$0=*&ppoly1:class Polygon * [line 42]\n n$1=_fun_Polygon_area(n$0:class Polygon *) virtual [line 42]\n *&return:int =(1 / (n$1 - 20)) [line 42]\n REMOVE_TEMPS(n$0,n$1); [line 42]\n NULLIFY(&ppoly1,false); [line 42]\n NULLIFY(&rect,false); [line 42]\n APPLY_ABSTRACTION; [line 42]\n " shape="box"] + + + 27 -> 26 ; +26 [label="26: Exit rect_area \n " color=yellow style=filled] + + +25 [label="25: Start rect_area\nFormals: \nLocals: ppoly1:class Polygon * rect:class Rectangle \n DECLARE_LOCALS(&return,&ppoly1,&rect); [line 38]\n NULLIFY(&ppoly1,false); [line 38]\n " color=yellow style=filled] + + + 25 -> 30 ; +24 [label="24: Constructor Init \n n$0=*&this:class Triangle * [line 29]\n _fun_Polygon_Polygon(n$0:class Triangle *) [line 29]\n REMOVE_TEMPS(n$0); [line 29]\n NULLIFY(&this,false); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] + + + 24 -> 23 ; +23 [label="23: Exit Triangle_Triangle \n " color=yellow style=filled] + + +22 [label="22: Start Triangle_Triangle\nFormals: this:class Triangle *\nLocals: \n DECLARE_LOCALS(&return); [line 29]\n " color=yellow style=filled] + + + 22 -> 24 ; +21 [label="21: DeclStmt \n n$1=*&this:class Triangle * [line 33]\n n$2=*n$1.width:int [line 33]\n n$3=*&this:class Triangle * [line 33]\n n$4=*n$3.height:int [line 33]\n *&x:int =(n$2 * n$4) [line 33]\n REMOVE_TEMPS(n$1,n$2,n$3,n$4); [line 33]\n NULLIFY(&this,false); [line 33]\n " shape="box"] + + + 21 -> 20 ; +20 [label="20: Return Stmt \n n$0=*&x:int [line 34]\n *&return:int =(n$0 - 10) [line 34]\n REMOVE_TEMPS(n$0); [line 34]\n NULLIFY(&x,false); [line 34]\n APPLY_ABSTRACTION; [line 34]\n " shape="box"] + + + 20 -> 19 ; +19 [label="19: Exit Triangle_area \n " color=yellow style=filled] + + +18 [label="18: Start Triangle_area\nFormals: this:class Triangle *\nLocals: x:int \n DECLARE_LOCALS(&return,&x); [line 32]\n NULLIFY(&x,false); [line 32]\n " color=yellow style=filled] + + + 18 -> 21 ; +17 [label="17: Exit Triangle_~Triangle \n " color=yellow style=filled] + + +16 [label="16: Start Triangle_~Triangle\nFormals: this:class Triangle *\nLocals: \n DECLARE_LOCALS(&return); [line 31]\n NULLIFY(&this,false); [line 31]\n " color=yellow style=filled] + + + 16 -> 17 ; +15 [label="15: Constructor Init \n n$0=*&this:class Rectangle * [line 23]\n _fun_Polygon_Polygon(n$0:class Rectangle *) [line 23]\n REMOVE_TEMPS(n$0); [line 23]\n NULLIFY(&this,false); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit Rectangle_Rectangle \n " color=yellow style=filled] + + +13 [label="13: Start Rectangle_Rectangle\nFormals: this:class Rectangle *\nLocals: \n DECLARE_LOCALS(&return); [line 23]\n " color=yellow style=filled] + + + 13 -> 15 ; +12 [label="12: Return Stmt \n n$0=*&this:class Rectangle * [line 26]\n n$1=*n$0.width:int [line 26]\n n$2=*&this:class Rectangle * [line 26]\n n$3=*n$2.height:int [line 26]\n *&return:int =(n$1 * n$3) [line 26]\n REMOVE_TEMPS(n$0,n$1,n$2,n$3); [line 26]\n NULLIFY(&this,false); [line 26]\n APPLY_ABSTRACTION; [line 26]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Exit Rectangle_area \n " color=yellow style=filled] + + +10 [label="10: Start Rectangle_area\nFormals: this:class Rectangle *\nLocals: \n DECLARE_LOCALS(&return); [line 26]\n " color=yellow style=filled] + + + 10 -> 12 ; +9 [label="9: Exit Polygon_Polygon \n " color=yellow style=filled] + + +8 [label="8: Start Polygon_Polygon\nFormals: this:class Polygon *\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n NULLIFY(&this,false); [line 10]\n " color=yellow style=filled] + + + 8 -> 9 ; +7 [label="7: Return Stmt \n *&return:int =0 [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] + + + 7 -> 6 ; +6 [label="6: Exit Polygon_area \n " color=yellow style=filled] + + +5 [label="5: Start Polygon_area\nFormals: this:class Polygon *\nLocals: \n DECLARE_LOCALS(&return); [line 20]\n NULLIFY(&this,false); [line 20]\n " color=yellow style=filled] + + + 5 -> 7 ; +4 [label="4: BinaryOperatorStmt: Assign \n n$2=*&this:class Polygon * [line 17]\n n$3=*&a:int [line 17]\n *n$2.width:int =n$3 [line 17]\n REMOVE_TEMPS(n$2,n$3); [line 17]\n NULLIFY(&a,false); [line 17]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: BinaryOperatorStmt: Assign \n n$0=*&this:class Polygon * [line 18]\n n$1=*&b:int [line 18]\n *n$0.height:int =n$1 [line 18]\n REMOVE_TEMPS(n$0,n$1); [line 18]\n NULLIFY(&b,false); [line 18]\n NULLIFY(&this,false); [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit Polygon_set_values \n " color=yellow style=filled] + + +1 [label="1: Start Polygon_set_values\nFormals: this:class Polygon * a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 16]\n " color=yellow style=filled] + + + 1 -> 4 ; +} diff --git a/infer/tests/codetoanalyze/objcpp/frontend/funcoverloading/af_test.dot b/infer/tests/codetoanalyze/objcpp/frontend/funcoverloading/af_test.dot index c033100aa..805bf9a1f 100644 --- a/infer/tests/codetoanalyze/objcpp/frontend/funcoverloading/af_test.dot +++ b/infer/tests/codetoanalyze/objcpp/frontend/funcoverloading/af_test.dot @@ -1,16 +1,16 @@ digraph iCFG { -6 [label="6: Return Stmt \n n$0=*&v:int [line 19]\n *&return:int =n$0 [line 19]\n REMOVE_TEMPS(n$0); [line 19]\n NULLIFY(&v,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] +6 [label="6: Return Stmt \n n$0=*&v:int [line 14]\n *&return:int =n$0 [line 14]\n REMOVE_TEMPS(n$0); [line 14]\n NULLIFY(&v,false); [line 14]\n APPLY_ABSTRACTION; [line 14]\n " shape="box"] 6 -> 5 ; 5 [label="5: Exit POPSelectValueType \n " color=yellow style=filled] -4 [label="4: Start POPSelectValueType\nFormals: v:int \nLocals: \n DECLARE_LOCALS(&return); [line 17]\n " color=yellow style=filled] +4 [label="4: Start POPSelectValueType\nFormals: v:int \nLocals: \n DECLARE_LOCALS(&return); [line 14]\n " color=yellow style=filled] 4 -> 6 ; -3 [label="3: Return Stmt \n *&return:int =1 [line 14]\n APPLY_ABSTRACTION; [line 14]\n " shape="box"] +3 [label="3: Return Stmt \n *&return:int =1 [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] 3 -> 2 ; diff --git a/infer/tests/endtoend/cpp/VirtualMethodsTest.java b/infer/tests/endtoend/cpp/VirtualMethodsTest.java new file mode 100644 index 000000000..f85c8eb0f --- /dev/null +++ b/infer/tests/endtoend/cpp/VirtualMethodsTest.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +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 VirtualMethodsTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/methods/virtual_methods.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 whenInferRunsOnMethodsDivideByZeroIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + String[] proceduresWithNullDeref = { + "rect_area", "tri_area", "poly_area", "tri_not_virtual_area" + }; + assertThat( + "Results should contain the no null dereference", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + proceduresWithNullDeref + ) + ); + } +} diff --git a/infer/tests/frontend/cpp/MethodsTest.java b/infer/tests/frontend/cpp/MethodsTest.java index feb270008..746207bde 100644 --- a/infer/tests/frontend/cpp/MethodsTest.java +++ b/infer/tests/frontend/cpp/MethodsTest.java @@ -70,4 +70,10 @@ public class MethodsTest { throws InterruptedException, IOException, InferException { frontendTest("conversion_operator.cpp"); } + + @Test + public void testVirtualMethodsDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("virtual_methods.cpp"); + } }