Add the virtual flag of C++ methods in method calls

Reviewed By: akotulski

Differential Revision: D2943884

fb-gh-sync-id: 33b1c8b
shipit-source-id: 33b1c8b
master
Dulma Rodriguez 9 years ago committed by facebook-github-bot-4
parent c585383fba
commit 2fb2b3d9e1

@ -1 +1 @@
Subproject commit d07910830a3ccf56c10a4eaf9c8dcad23ecb4d9b
Subproject commit 60b10ef73606e814cf87c08e5c0d263dc1f9fb8c

@ -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;

@ -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

@ -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

@ -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

@ -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 *)

@ -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

@ -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 = &rect;
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;
}

@ -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 ;
}

@ -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 ;

@ -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<String> 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
)
);
}
}

@ -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");
}
}

Loading…
Cancel
Save