diff --git a/infer/src/IR/CallFlags.ml b/infer/src/IR/CallFlags.ml index cd2a8dbf0..34852948d 100644 --- a/infer/src/IR/CallFlags.ml +++ b/infer/src/IR/CallFlags.ml @@ -20,12 +20,14 @@ type t = ; cf_interface: bool ; cf_noreturn: bool ; cf_is_objc_block: bool - ; cf_targets: Typ.Procname.t list } + ; cf_targets: Typ.Procname.t list + ; cf_with_block_parameters: bool } [@@deriving compare] let pp f cf = if cf.cf_virtual then F.fprintf f " virtual" ; - if cf.cf_noreturn then F.fprintf f " noreturn" + if cf.cf_noreturn then F.fprintf f " noreturn" ; + if cf.cf_with_block_parameters then F.fprintf f " block_params" let default = @@ -33,5 +35,6 @@ let default = ; cf_interface= false ; cf_noreturn= false ; cf_is_objc_block= false + ; cf_with_block_parameters= false ; cf_targets= [] } diff --git a/infer/src/IR/CallFlags.mli b/infer/src/IR/CallFlags.mli index e557f4547..b55c23eaa 100644 --- a/infer/src/IR/CallFlags.mli +++ b/infer/src/IR/CallFlags.mli @@ -20,7 +20,8 @@ type t = ; cf_interface: bool ; cf_noreturn: bool ; cf_is_objc_block: bool - ; cf_targets: Typ.Procname.t list } + ; cf_targets: Typ.Procname.t list + ; cf_with_block_parameters: bool } [@@deriving compare] val pp : F.formatter -> t -> unit diff --git a/infer/src/IR/Exp.ml b/infer/src/IR/Exp.ml index 78dc13d08..a2eb35513 100644 --- a/infer/src/IR/Exp.ml +++ b/infer/src/IR/Exp.ml @@ -271,3 +271,9 @@ let pp_printenv pe pp_typ f e = pp_ pe (pp_typ pe) f e let pp f e = pp_printenv Pp.text Typ.pp f e let to_string e = F.asprintf "%a" pp e + +let is_objc_block_closure = function + | Closure {name} -> + Typ.Procname.is_objc_block name + | _ -> + false diff --git a/infer/src/IR/Exp.mli b/infer/src/IR/Exp.mli index a1e3ebe92..153e48e4c 100644 --- a/infer/src/IR/Exp.mli +++ b/infer/src/IR/Exp.mli @@ -133,3 +133,5 @@ val pp_printenv : Pp.env -> (Pp.env -> F.formatter -> Typ.t -> unit) -> F.format val pp : F.formatter -> t -> unit val to_string : t -> string + +val is_objc_block_closure : t -> bool diff --git a/infer/src/IR/Sil.ml b/infer/src/IR/Sil.ml index a075890f4..b7341e26d 100644 --- a/infer/src/IR/Sil.ml +++ b/infer/src/IR/Sil.ml @@ -437,6 +437,20 @@ let pp_instr pe0 f instr = color_post_wrapper changed pe0 f +let add_with_block_parameters_flag instr = + match instr with + | Call (ret_id, Exp.Const Const.Cfun name, arg_ts, loc, cf) -> + if List.exists ~f:(fun (exp, _) -> Exp.is_objc_block_closure exp) arg_ts + && Typ.Procname.is_objc_method name + (* to be extended to other methods *) + then + let cf' = {cf with cf_with_block_parameters= true} in + Call (ret_id, Exp.Const (Const.Cfun name), arg_ts, loc, cf') + else instr + | _ -> + instr + + (** Check if a pvar is a local pointing to a block in objc *) let is_block_pvar pvar = Typ.has_block_prefix (Mangled.to_string (Pvar.get_name pvar)) diff --git a/infer/src/IR/Sil.mli b/infer/src/IR/Sil.mli index 330598a5d..fae0cc903 100644 --- a/infer/src/IR/Sil.mli +++ b/infer/src/IR/Sil.mli @@ -276,6 +276,11 @@ val block_pvar : Pvar.t val is_block_pvar : Pvar.t -> bool (** Check if a pvar is a local pointing to a block in objc *) +val add_with_block_parameters_flag : instr -> instr +(** Adds a with_blocks_parameters flag to a method call, when the arguments + contain an Objective-C block, and the method is an Objective-C method + (to be extended to other methods) *) + val hpred_get_lhs : hpred -> Exp.t (** Return the lhs expression of a hpred *) diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 85b4c85aa..d39d42f77 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -898,6 +898,8 @@ module Procname = struct false + let is_objc_method = function ObjC_Cpp {kind} -> is_objc_kind kind | _ -> false + (** [is_constructor pname] returns true if [pname] is a constructor *) let is_constructor = function | Java js -> diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 0e7af475f..f143a6073 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -353,6 +353,9 @@ module Procname : sig val is_objc_constructor : string -> bool (** Check if this is a constructor method in Objective-C. *) + val is_objc_method : t -> bool + (** Check if this is an Objective-C method. *) + val is_constructor : t -> bool (** Check if this is a constructor. *) diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 11ff289eb..eec8f6d9c 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -256,7 +256,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s (ret_id, params_sil, [], match ret_id with Some (i, t) -> [(Exp.Var i, t)] | None -> []) in let call_instr = Sil.Call (ret_id', function_sil, params, sil_loc, call_flags) in - {empty_res_trans with instrs= [call_instr]; exps= ret_exps; initd_exps} + let call_instr' = Sil.add_with_block_parameters_flag call_instr in + {empty_res_trans with instrs= [call_instr']; exps= ret_exps; initd_exps} let stringLiteral_trans trans_state expr_info str = @@ -1125,7 +1126,7 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s match class_type.desc with | Typ.Tptr _ -> (* do not inject the extra dereference for procedures generated to record the - initialization code of globals *) + initialization code of globals *) Procdesc.get_proc_name trans_state.context.procdesc |> Typ.Procname.get_global_name_of_initializer |> Option.is_none | _ -> diff --git a/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot b/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot index 361a37c46..012e97a43 100644 --- a/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot +++ b/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot @@ -41,7 +41,7 @@ digraph iCFG { "capture#A#instance.d411336575e4bf632a1828f5f5979726_2" [label="2: Exit A_capture \n " color=yellow style=filled] -"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3: Message Call: sHandler: \n n$0=*&self:A* [line 47, column 4]\n n$1=*n$0._b:B* [line 47, column 4]\n n$2=*&self:A* [line 47, column 16]\n _fun_B_sHandler:(n$1:B*,(_fun_objc_blockA_capture_1,n$2):_fn_(*)) virtual [line 47, column 3]\n " shape="box"] +"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3: Message Call: sHandler: \n n$0=*&self:A* [line 47, column 4]\n n$1=*n$0._b:B* [line 47, column 4]\n n$2=*&self:A* [line 47, column 16]\n _fun_B_sHandler:(n$1:B*,(_fun_objc_blockA_capture_1,n$2):_fn_(*)) virtual block_params [line 47, column 3]\n " shape="box"] "capture#A#instance.d411336575e4bf632a1828f5f5979726_3" -> "capture#A#instance.d411336575e4bf632a1828f5f5979726_2" ; diff --git a/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot b/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot index 562af8a9e..9a444d1c5 100644 --- a/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot @@ -11,7 +11,7 @@ digraph iCFG { "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_2" ; -"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4: Message Call: foo:and: \n n$2=*&self:B* [line 25, column 10]\n n$3=*n$2.h:int [line 25, column 10]\n n$4=*&self:B* const [line 26, column 11]\n _fun_B_foo:and:(n$3:int,(_fun_objc_blockB_f_1,n$4):_fn_(*)) [line 25, column 3]\n " shape="box"] +"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4: Message Call: foo:and: \n n$2=*&self:B* [line 25, column 10]\n n$3=*n$2.h:int [line 25, column 10]\n n$4=*&self:B* const [line 26, column 11]\n _fun_B_foo:and:(n$3:int,(_fun_objc_blockB_f_1,n$4):_fn_(*)) block_params [line 25, column 3]\n " shape="box"] "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" ; diff --git a/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot b/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot index 2c7d110ad..a3bf9edf6 100644 --- a/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot +++ b/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot @@ -63,7 +63,7 @@ digraph iCFG { "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" [label="2: Exit MyBlock_array \n " color=yellow style=filled] -"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3: Message Call: enumerateObjectsUsingBlock: \n n$0=*&a:NSArray* [line 21, column 4]\n _fun_NSArray_enumerateObjectsUsingBlock:(n$0:NSArray*,(_fun_objc_blockMyBlock_array_1):_fn_(*)) virtual [line 21, column 3]\n " shape="box"] +"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3: Message Call: enumerateObjectsUsingBlock: \n n$0=*&a:NSArray* [line 21, column 4]\n _fun_NSArray_enumerateObjectsUsingBlock:(n$0:NSArray*,(_fun_objc_blockMyBlock_array_1):_fn_(*)) virtual block_params [line 21, column 3]\n " shape="box"] "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" -> "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" ;