From b9a56a6c52dcee89e4f35a491c87ef3329e2bcb2 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Mon, 13 Nov 2017 02:55:05 -0800 Subject: [PATCH] [clang] Specialize also c functions with blocks as parameters Reviewed By: jberdine Differential Revision: D6273764 fbshipit-source-id: eab549f --- infer/src/IR/Cfg.ml | 14 ++++++++-- infer/src/IR/Sil.ml | 2 +- infer/src/IR/Typ.ml | 2 ++ infer/src/IR/Typ.mli | 11 +++++--- .../codetoanalyze/objc_getters_setters/A.h | 9 +++++++ .../codetoanalyze/objc_getters_setters/B.m | 27 +++++++++++++++++++ .../objc_getters_setters/issues.exp | 1 + 7 files changed, 59 insertions(+), 7 deletions(-) diff --git a/infer/src/IR/Cfg.ml b/infer/src/IR/Cfg.ml index 75b978d27..fd201b570 100644 --- a/infer/src/IR/Cfg.ml +++ b/infer/src/IR/Cfg.ml @@ -580,14 +580,24 @@ let specialize_with_block_args callee_pdesc pname_with_block_args block_args = in List.unzip new_formals_blocks_captured_vars_with_annots in + let source_file_captured = + let pname = Procdesc.get_proc_name callee_pdesc in + match Attributes.find_file_capturing_procedure pname with + | Some (source_file, _) -> + source_file + | None -> + Logging.die InternalError + "specialize_with_block_args ahould only be called with defined procedures, but we cannot find the captured file of procname %a" + Typ.Procname.pp pname + in let resolved_attributes = { callee_attributes with proc_name= pname_with_block_args ; is_defined= true ; err_log= Errlog.empty () - ; source_file_captured= callee_attributes.loc.Location.file ; formals= new_formals_blocks_captured_vars - ; method_annotation= (fst callee_attributes.method_annotation, extended_formals_annots) } + ; method_annotation= (fst callee_attributes.method_annotation, extended_formals_annots) + ; source_file_captured } in Attributes.store resolved_attributes ; let resolved_pdesc = diff --git a/infer/src/IR/Sil.ml b/infer/src/IR/Sil.ml index 7b64b7bc4..00c1ddab3 100644 --- a/infer/src/IR/Sil.ml +++ b/infer/src/IR/Sil.ml @@ -441,7 +441,7 @@ 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 + && (Typ.Procname.is_objc_method name || Typ.Procname.is_c_function name) (* to be extended to other methods *) then let cf' = {cf with cf_with_block_parameters= true} in diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 12c8fb753..84c347ce9 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -670,6 +670,8 @@ module Procname = struct let is_c_method = function ObjC_Cpp _ -> true | _ -> false + let is_c_function = function C _ -> true | _ -> false + let is_obj_c_pp = function ObjC_Cpp _ | C _ -> true | _ -> false let is_constexpr = function ObjC_Cpp {kind= CPPConstructor (_, true)} -> true | _ -> false diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index df08d4427..5ecad9ba5 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -274,11 +274,11 @@ module Procname : sig (** Type of Objective C block names. *) type block_name - (** Type of procedure names. - WithBlockParameters is used for creating an instantiation of a method that contains block parameters - and it's called with concrete blocks. For example: + (** Type of procedure names. + WithBlockParameters is used for creating an instantiation of a method that contains block parameters + and it's called with concrete blocks. For example: foo(Block block) {block();} - bar() {foo(my_block)} is executed as foo_my_block() {my_block(); } + bar() {foo(my_block)} is executed as foo_my_block() {my_block(); } where foo_my_block is created with WithBlockParameters (foo, [my_block]) *) type t = | Java of java @@ -360,6 +360,9 @@ module Procname : sig val is_c_method : t -> bool (** Check if this is an Objective-C/C++ method name. *) + val is_c_function : t -> bool + (** Check if this is a C function name. *) + val is_obj_c_pp : t -> bool (** Check if this is an Objective-C/C++ method name or C-style function. *) diff --git a/infer/tests/build_systems/codetoanalyze/objc_getters_setters/A.h b/infer/tests/build_systems/codetoanalyze/objc_getters_setters/A.h index 86ba954bb..fc6d18de6 100644 --- a/infer/tests/build_systems/codetoanalyze/objc_getters_setters/A.h +++ b/infer/tests/build_systems/codetoanalyze/objc_getters_setters/A.h @@ -23,3 +23,12 @@ typedef void (^MyBlock)(int x); and:(nullable NSString*)name; @end + +void c_function(_Nonnull MyBlock block1, + _Nonnull MyBlock block2, + NSString* _Nonnull name) { + block1(22); + int my_var = 11; + block2(33); + int my_other_var = 12; +}; diff --git a/infer/tests/build_systems/codetoanalyze/objc_getters_setters/B.m b/infer/tests/build_systems/codetoanalyze/objc_getters_setters/B.m index 4dd72c17c..494bdc289 100644 --- a/infer/tests/build_systems/codetoanalyze/objc_getters_setters/B.m +++ b/infer/tests/build_systems/codetoanalyze/objc_getters_setters/B.m @@ -73,4 +73,31 @@ return *p; // and not here } } + +- (int)calling_c_function_with_block_parameters { + int h = 10; + int z = 10; + c_function( + ^(int i) { + self->x = i; + }, + ^(int i) { + self->y = h + z; + }, + @"Hi"); + return self->y; +} + ++ (int)calling_c_function_with_block_parameters_sets_fields_correctly { + B* b = [B new]; + [b calling_c_function_with_block_parameters]; + if (b->x + b->y == 42) { + int* p = 0; + return *p; // NPE here, because we know that the values x and y + // are set correctly by calling blocks + } else { + int* p = 0; + return *p; // and not here + } +} @end diff --git a/infer/tests/build_systems/objc_getters_setters/issues.exp b/infer/tests/build_systems/objc_getters_setters/issues.exp index ef4ca2cc4..d5b1e79eb 100644 --- a/infer/tests/build_systems/objc_getters_setters/issues.exp +++ b/infer/tests/build_systems/objc_getters_setters/issues.exp @@ -1,3 +1,4 @@ +build_systems/codetoanalyze/objc_getters_setters/B.m, B_calling_c_function_with_block_parameters_sets_fields_correctly, 5, NULL_DEREFERENCE, [start of procedure calling_c_function_with_block_parameters_sets_fields_correctly,start of procedure calling_c_function_with_block_parameters,start of procedure c_function(),start of procedure block,return from a call to objc_blockB_calling_c_function_with_block_parameters_3,start of procedure block,return from a call to objc_blockB_calling_c_function_with_block_parameters_4,return from a call to c_function_objc_blockB_calling_c_function_with_block_parameters_3_objc_blockB_calling_c_function_with_block_parameters_4,return from a call to B_calling_c_function_with_block_parameters,Condition is true] build_systems/codetoanalyze/objc_getters_setters/B.m, B_calling_method_with_block_parameters_sets_fields_correctly, 5, NULL_DEREFERENCE, [start of procedure calling_method_with_block_parameters_sets_fields_correctly,start of procedure calling_method_with_block_parameters,start of procedure foo:and:and_also:and:,start of procedure block,return from a call to objc_blockB_calling_method_with_block_parameters_1,start of procedure block,return from a call to objc_blockB_calling_method_with_block_parameters_2,return from a call to A_foo:and:and_also:and:_objc_blockB_calling_method_with_block_parameters_1_objc_blockB_calling_method_with_block_parameters_2,return from a call to B_calling_method_with_block_parameters,Condition is true] build_systems/codetoanalyze/objc_getters_setters/B.m, B_npe_no_bad_footprint_in_getter:, 3, NULL_DEREFERENCE, [start of procedure npe_no_bad_footprint_in_getter:] build_systems/codetoanalyze/objc_getters_setters/B.m, B_npe_no_bad_footprint_in_setter:andMetadata:, 3, NULL_DEREFERENCE, [start of procedure npe_no_bad_footprint_in_setter:andMetadata:]