diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index 12cefcb9e..ec20740c4 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -26,6 +26,10 @@ let dummy_stmt_info () = Clang_ast_t.si_source_range = dummy_source_range () } +(* given a stmt_info return the same stmt_info with a fresh pointer *) +let fresh_stmt_info stmt_info = + { stmt_info with Clang_ast_t.si_pointer = Ast_utils.get_fresh_pointer () } + let dummy_decl_info decl_info = { decl_info with @@ -48,6 +52,14 @@ let empty_decl_info = { Clang_ast_t.di_full_comment = None; } +let empty_var_decl_info = { + Clang_ast_t.vdi_storage_class = None; + Clang_ast_t.vdi_tls_kind =`Tls_none; + Clang_ast_t.vdi_is_module_private = false; + Clang_ast_t.vdi_is_nrvo_variable = false; + Clang_ast_t.vdi_init_expr = None; +} + let stmt_info_with_fresh_pointer stmt_info = { Clang_ast_t.si_pointer = Ast_utils.get_fresh_pointer (); @@ -71,6 +83,14 @@ let create_id_type () = create_qual_type "id" let create_char_type () = create_qual_type "char *" +let create_BOOL_type () = { qt_raw = "BOOL"; qt_desugared = Some("signed char") } + +let create_void_unsigned_long_type () = create_qual_type "void *(unsigned long)" + +let create_unsigned_long_type () = create_qual_type "unsigned long" + +let create_void_void_type () = create_qual_type "void (void *)" + let create_integer_literal stmt_info n = let stmt_info = dummy_stmt_info () in let expr_info = { @@ -132,11 +152,17 @@ let dummy_stmt () = let make_stmt_info di = { Clang_ast_t.si_pointer = di.Clang_ast_t.di_pointer; Clang_ast_t.si_source_range = di.Clang_ast_t.di_source_range } -let make_expr_info_with_objc_kind qt objc_kind = { +let make_expr_info qt vk objc_kind = { Clang_ast_t.ei_qual_type = qt; - Clang_ast_t.ei_value_kind = `LValue; + Clang_ast_t.ei_value_kind = vk; Clang_ast_t.ei_object_kind = objc_kind;} +let make_expr_info_with_objc_kind qt objc_kind = + make_expr_info qt `LValue objc_kind + +let make_lvalue_obc_prop_expr_info qt = + make_expr_info qt `LValue `ObjCProperty + let make_method_decl_info mdi body = { Clang_ast_t.omdi_is_instance_method = mdi.Clang_ast_t.omdi_is_instance_method; Clang_ast_t.omdi_result_type = mdi.Clang_ast_t.omdi_result_type; @@ -168,14 +194,23 @@ let make_name_decl name = { Clang_ast_t.ni_qual_name = [name]; } -let make_general_decl_ref k decl_ptr name is_hidden qt = { +let make_decl_ref k decl_ptr name is_hidden qt_opt = { Clang_ast_t.dr_kind = k; Clang_ast_t.dr_decl_pointer = decl_ptr; Clang_ast_t.dr_name = Some (make_name_decl name); Clang_ast_t.dr_is_hidden = is_hidden ; - Clang_ast_t.dr_qual_type = Some (qt) + Clang_ast_t.dr_qual_type = qt_opt } +let make_decl_ref_qt k decl_ptr name is_hidden qt = + make_decl_ref k decl_ptr name is_hidden (Some qt) + +let make_decl_ref_no_qt k decl_ptr name is_hidden = + make_decl_ref k decl_ptr name is_hidden None + +let make_decl_ref_invalid k name is_hidden qt = + make_decl_ref k (Ast_utils.get_invalid_pointer ()) name is_hidden (Some qt) + let make_decl_ref_self ptr qt = { Clang_ast_t.dr_kind = `ImplicitParam; Clang_ast_t.dr_decl_pointer = ptr; @@ -190,7 +225,7 @@ let make_decl_ref_expr_info decl_ref = { } let make_obj_c_ivar_ref_expr_info k ptr n qt = { - Clang_ast_t.ovrei_decl_ref = make_general_decl_ref k ptr n false qt; + Clang_ast_t.ovrei_decl_ref = make_decl_ref_qt k ptr n false qt; Clang_ast_t.ovrei_pointer = Ast_utils.get_fresh_pointer (); Clang_ast_t.ovrei_is_free_ivar = true; } @@ -212,9 +247,8 @@ let make_self_field class_type di qt field_name = let qt_class = create_qual_type class_type in let expr_info = make_expr_info_with_objc_kind qt `ObjCProperty in let stmt_info = make_stmt_info di in - let decl_ptr = Ast_utils.get_invalid_pointer() in - let cast_exp = make_cast_expr qt_class di (make_decl_ref_expr_info (make_decl_ref_self decl_ptr qt_class)) `ObjCProperty in - let obj_c_ivar_ref_expr_info = make_obj_c_ivar_ref_expr_info (`ObjCIvar) decl_ptr field_name qt in + let cast_exp = make_cast_expr qt_class di (make_decl_ref_expr_info (make_decl_ref_self di.di_pointer qt_class)) `ObjCProperty in + let obj_c_ivar_ref_expr_info = make_obj_c_ivar_ref_expr_info (`ObjCIvar) di.di_pointer field_name qt in let ivar_ref_exp = ObjCIvarRefExpr(stmt_info, [cast_exp], expr_info, obj_c_ivar_ref_expr_info) in ivar_ref_exp @@ -258,9 +292,20 @@ let make_expr_info qt = Clang_ast_t.ei_object_kind = `ObjCProperty } +let make_general_expr_info qt vk ok = + { + Clang_ast_t.ei_qual_type = qt; + Clang_ast_t.ei_value_kind = vk; + Clang_ast_t.ei_object_kind = ok + } + +let make_ObjCBoolLiteralExpr stmt_info value = + let ei = make_expr_info (create_BOOL_type ()) in + ObjCBoolLiteralExpr((fresh_stmt_info stmt_info),[], ei, value) + let make_decl_ref_exp_var (var_name, var_qt, var_ptr) var_kind stmt_info = let stmt_info = stmt_info_with_fresh_pointer stmt_info in - let decl_ref = make_general_decl_ref var_kind var_ptr var_name false var_qt in + let decl_ref = make_decl_ref_qt var_kind var_ptr var_name false var_qt in let expr_info = make_expr_info var_qt in make_decl_ref_exp stmt_info expr_info (make_decl_ref_expr_info decl_ref) @@ -272,7 +317,7 @@ let make_message_expr param_qt selector decl_ref_exp stmt_info add_cast = [cast_expr] else [decl_ref_exp] in let obj_c_message_expr_info = make_obj_c_message_expr_info_instance selector in - let expr_info = make_expr_info param_qt in + let expr_info = make_expr_info_with_objc_kind param_qt `ObjCProperty in ObjCMessageExpr (stmt_info, parameters, expr_info, obj_c_message_expr_info) let make_compound_stmt stmts stmt_info = @@ -289,18 +334,18 @@ let make_next_object_exp stmt_info item items = | DeclStmt (stmt_info, _, [VarDecl(di, name_info, var_type, _)]) -> let var_name = name_info.Clang_ast_t.ni_name in let decl_ptr = di.Clang_ast_t.di_pointer in - let decl_ref = make_general_decl_ref `Var decl_ptr var_name false var_type in + let decl_ref = make_decl_ref_qt `Var decl_ptr var_name false var_type in let stmt_info_var = { si_pointer = di.Clang_ast_t.di_pointer; si_source_range = di.Clang_ast_t.di_source_range } in - DeclRefExpr(stmt_info_var, [], (make_expr_info var_type), (make_decl_ref_expr_info decl_ref)), + DeclRefExpr(stmt_info_var, [], (make_expr_info_with_objc_kind var_type `ObjCProperty), (make_decl_ref_expr_info decl_ref)), var_type | _ -> assert false in let message_call = make_message_expr (create_qual_type CFrontend_config.id_cl) CFrontend_config.next_object items stmt_info false in let boi = { Clang_ast_t.boi_kind = `Assign } in - make_binary_stmt var_decl_ref message_call stmt_info (make_expr_info var_type) boi + make_binary_stmt var_decl_ref message_call stmt_info (make_expr_info_with_objc_kind var_type `ObjCProperty) boi let empty_var_decl = { vdi_storage_class = None; @@ -317,7 +362,8 @@ let translate_dispatch_function block_name stmt_info stmt_list ei n = try Utils.list_nth stmt_list (n + 1) with Not_found -> assert false in let block_name_info = make_name_decl block_name in - match block_expr with BlockExpr(bsi, bsl, bei, bd) -> + match block_expr with + | BlockExpr(bsi, bsl, bei, bd) -> let qt = bei.Clang_ast_t.ei_qual_type in let cast_info = { cei_cast_kind = `BitCast; cei_base_path =[]} in let block_def = ImplicitCastExpr(stmt_info,[block_expr], bei, cast_info) in @@ -326,27 +372,11 @@ let translate_dispatch_function block_name stmt_info stmt_list ei n = let var_decl_info = { empty_var_decl with vdi_init_expr = Some block_def } in let block_var_decl = VarDecl(decl_info, block_name_info, ei.ei_qual_type, var_decl_info) in let decl_stmt = DeclStmt(stmt_info,[], [block_var_decl]) in - let expr_info_call = { - Clang_ast_t.ei_qual_type = create_void_type (); - Clang_ast_t.ei_value_kind = `XValue; - Clang_ast_t.ei_object_kind = `Ordinary - } in - let expr_info_dre = { - Clang_ast_t.ei_qual_type = qt; - Clang_ast_t.ei_value_kind = `LValue; - Clang_ast_t.ei_object_kind = `Ordinary - } in - let decl_ref = { - dr_kind = `Var; - dr_decl_pointer = decl_info.di_pointer; - dr_name = Some block_name_info; - dr_is_hidden = false; - dr_qual_type = Some qt; - } in - let decl_ref_expr_info = { - drti_decl_ref = Some decl_ref; - drti_found_decl_ref = None - } in + + let expr_info_call = make_general_expr_info (create_void_type ()) `XValue `Ordinary in + let expr_info_dre = make_expr_info_with_objc_kind qt `Ordinary in + let decl_ref = make_decl_ref_qt `Var stmt_info.si_pointer block_name false qt in + let decl_ref_expr_info = make_decl_ref_expr_info decl_ref in let cast_info_call = { cei_cast_kind = `LValueToRValue; cei_base_path =[]} in let decl_ref_exp = DeclRefExpr(stmt_info, [], expr_info_dre, decl_ref_expr_info) in let stmt_call = ImplicitCastExpr(stmt_info, [decl_ref_exp], bei, cast_info_call) in @@ -354,6 +384,256 @@ let translate_dispatch_function block_name stmt_info stmt_list ei n = CompoundStmt (stmt_info, [decl_stmt; call_block_var]), qt | _ -> assert false (* when we call this function we have already checked that this cannot be possible *) +(* Create declaration statement: qt vname = iexp *) +let make_DeclStmt stmt_info di qt vname iexp = + let init_expr_opt, init_expr_l = match iexp with + | Some iexp' -> + let ie = create_implicit_cast_expr stmt_info [iexp'] qt `IntegralCast in + Some ie, [ie] + | None -> None, [] in + let var_decl = VarDecl(di, vname, qt, { empty_var_decl_info with Clang_ast_t.vdi_init_expr = init_expr_opt;}) in + DeclStmt(stmt_info, init_expr_l, [var_decl]) + +let build_OpaqueValueExpr si source_expr ei = + let opaque_value_expr_info = { Clang_ast_t.ovei_source_expr = Some source_expr } in + OpaqueValueExpr(si, [], ei, opaque_value_expr_info) + +let pseudo_object_qt () = + create_qual_type CFrontend_config.pseudo_object_type + +(* Create expression PseudoObjectExpr for 'o.m' *) +let build_PseudoObjectExpr qt_m o_cast_decl_ref_exp mname = + match o_cast_decl_ref_exp with + | ImplicitCastExpr(si, stmt_list, ei, cast_expr_info) -> + let ove = build_OpaqueValueExpr si o_cast_decl_ref_exp ei in + let ei_opre = make_expr_info (pseudo_object_qt ()) in + let obj_c_property_ref_expr_info = { + Clang_ast_t.oprei_kind = + `PropertyRef (make_decl_ref_no_qt `ObjCProperty si.si_pointer CFrontend_config.count false); + Clang_ast_t.oprei_is_super_receiver = false; + Clang_ast_t.oprei_is_messaging_getter = true; + Clang_ast_t.oprei_is_messaging_setter = false; + } in + let opre = ObjCPropertyRefExpr(si, [ove], ei_opre, obj_c_property_ref_expr_info) in + let ome = make_message_expr qt_m mname o_cast_decl_ref_exp si false in + let poe_ei = make_general_expr_info qt_m `LValue `Ordinary in + PseudoObjectExpr(si, [opre; ove; ome], poe_ei) + | _ -> assert false + +let create_call stmt_info decl_pointer function_name qt parameters = + let expr_info_call = { + Clang_ast_t.ei_qual_type = create_void_type (); + Clang_ast_t.ei_value_kind = `XValue; + Clang_ast_t.ei_object_kind = `Ordinary + } in + let expr_info_dre = make_expr_info_with_objc_kind qt `Ordinary in + let decl_ref = make_decl_ref_qt `Function decl_pointer function_name false qt in + let decl_ref_info = make_decl_ref_expr_info decl_ref in + let decl_ref_exp = DeclRefExpr(stmt_info, [], expr_info_dre, decl_ref_info) in + let cast = create_implicit_cast_expr (fresh_stmt_info stmt_info) [decl_ref_exp] qt `FunctionToPointerDecay in + CallExpr(stmt_info, cast:: parameters, expr_info_call) + +(* For a of type NSArray* Translate *) +(* [a enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL * stop) { *) +(* body_block *) +(* }; *) +(* ]; *) + +(* as follows: *) + +(* NSArray *objects = a; *) +(* void (^enumerateObjectsUsingBlock)(id, NSUInteger, BOOL* )= ^(id object, NSUInteger idx, BOOL* stop) { *) +(* body_block *) +(* }; *) +(* BOOL *stop = malloc(sizeof(BOOL)); *) +(* *stop = NO; *) + +(* for (NSUInteger idx=0; idx [] + | ParmVarDecl(di, name, qt, _):: lp' -> + (name.Clang_ast_t.ni_name, di.Clang_ast_t.di_pointer, qt):: get_name_pointers lp' + | _ -> assert false in + + let build_idx_decl pidx = + match pidx with + | ParmVarDecl(di_idx, name_idx, qt_idx, _) -> + let zero = create_integer_literal stmt_info "0" in + (* qt_idx idx = 0; *) + let idx_decl_stmt = make_DeclStmt (fresh_stmt_info stmt_info) di_idx qt_idx name_idx (Some zero) in + let idx_ei = make_expr_info qt_idx in + let idx_decl_ref = make_decl_ref_qt `Var di_idx.di_pointer name_idx.Clang_ast_t.ni_name false qt_idx in + let idx_drei = make_decl_ref_expr_info idx_decl_ref in + let idx_decl_ref_exp = make_decl_ref_exp stmt_info idx_ei idx_drei in + let idx_cast = create_implicit_cast_expr (fresh_stmt_info stmt_info) [idx_decl_ref_exp] qt_idx `LValueToRValue in + idx_decl_stmt, idx_decl_ref_exp, idx_cast, qt_idx + | _ -> assert false in + + let cast_expr decl_ref qt = + let ei = make_expr_info qt in + let drei = make_decl_ref_expr_info decl_ref in + let decl_ref_exp = make_decl_ref_exp (fresh_stmt_info stmt_info) ei drei in + create_implicit_cast_expr (fresh_stmt_info stmt_info) [decl_ref_exp] qt `LValueToRValue in + + (* build statement BOOL *stop = malloc(sizeof(BOOL)); *) + let build_stop pstop = + match pstop with + | ParmVarDecl(di, name, qt, _) -> + let qt_fun = create_void_unsigned_long_type () in + let parameter = UnaryExprOrTypeTraitExpr((fresh_stmt_info stmt_info), [], + make_expr_info (create_unsigned_long_type ()), + { Clang_ast_t.uttei_kind = `SizeOf; Clang_ast_t.uttei_qual_type = Some (create_BOOL_type ()) }) in + let malloc = create_call (fresh_stmt_info stmt_info) di.di_pointer CFrontend_config.malloc qt_fun [parameter] in + let init_exp = create_implicit_cast_expr (fresh_stmt_info stmt_info) [malloc] qt `BitCast in + make_DeclStmt (fresh_stmt_info stmt_info) di qt name (Some init_exp) + | _ -> assert false in + + (* BOOL *stop =NO; *) + let stop_equal_no pstop = + match pstop with + | ParmVarDecl(di, name, qt, _) -> + let decl_ref = make_decl_ref_qt `Var di.di_pointer name.Clang_ast_t.ni_name false qt in + let cast = cast_expr decl_ref qt in + let lhs = UnaryOperator((fresh_stmt_info stmt_info), [cast], ei, { uoi_kind = `Deref; uoi_is_postfix = true }) in + let bool_NO = make_ObjCBoolLiteralExpr stmt_info 0 in + BinaryOperator((fresh_stmt_info stmt_info), [lhs; bool_NO], ei, { boi_kind = `Assign }) + | _ -> assert false in + + (* build statement free(stop); *) + let free_stop pstop = + match pstop with + | ParmVarDecl(di, name, qt, _) -> + let qt_fun = create_void_void_type () in + let decl_ref = make_decl_ref_qt `Var di.di_pointer name.Clang_ast_t.ni_name false qt in + let cast = cast_expr decl_ref qt in + let parameter = + create_implicit_cast_expr (fresh_stmt_info stmt_info) [cast] (create_void_type ()) `BitCast in + create_call (fresh_stmt_info stmt_info) di.di_pointer CFrontend_config.free qt_fun [parameter] + | _ -> assert false in + + (* idx ei + | _ -> assert false in + + (* id object= objects[idx]; *) + let build_object_DeclStmt pobj decl_ref_expr_array decl_ref_expr_idx qt_idx = + match pobj with + | ParmVarDecl(di_obj, name_obj, qt_obj, _) -> + let poe_ei = make_general_expr_info qt_obj `LValue `Ordinary in + let ei_array = get_ei_from_cast decl_ref_expr_array in + let ove_array = build_OpaqueValueExpr (fresh_stmt_info stmt_info) decl_ref_expr_array ei_array in + let ei_idx = get_ei_from_cast decl_ref_expr_idx in + let ove_idx = build_OpaqueValueExpr (fresh_stmt_info stmt_info) decl_ref_expr_idx ei_idx in + let objc_sre = ObjCSubscriptRefExpr((fresh_stmt_info stmt_info), [ove_array; ove_idx], + make_expr_info (pseudo_object_qt ()), + { osrei_kind =`ArraySubscript; osrei_getter = None; osrei_setter = None; }) in + let obj_c_message_expr_info = { omei_selector = CFrontend_config.object_at_indexed_subscript_m; omei_receiver_kind =`Instance } in + let ome = ObjCMessageExpr((fresh_stmt_info stmt_info), [ove_array; ove_idx], poe_ei, obj_c_message_expr_info) in + let pseudo_obj_expr = PseudoObjectExpr((fresh_stmt_info stmt_info), [objc_sre; ove_array; ove_idx; ome], poe_ei) in + let vdi = { empty_var_decl_info with vdi_init_expr = Some (pseudo_obj_expr) } in + let var_decl = VarDecl(di_obj, name_obj, qt_obj, vdi) in + DeclStmt((fresh_stmt_info stmt_info), [pseudo_obj_expr], [var_decl]) + | _ -> assert false in + + (* NSArray *objects = a *) + let objects_array_DeclStmt init = + let di = { empty_decl_info with Clang_ast_t.di_pointer = Ast_utils.get_fresh_pointer () } in + let qt = create_qual_type CFrontend_config.ns_array_ptr in + (* init should be ImplicitCastExpr of array a *) + let vdi = { empty_var_decl_info with vdi_init_expr = Some (init) } in + let var_decl = VarDecl(di, make_name_decl CFrontend_config.objects, qt, vdi) in + DeclStmt((fresh_stmt_info stmt_info), [init], [var_decl]), [(CFrontend_config.objects, di.Clang_ast_t.di_pointer, qt)] in + + let make_object_cast_decl_ref_expr objects = + match objects with + | DeclStmt(si, _, [VarDecl(di, name, qt, vdi)]) -> + let decl_ref = make_decl_ref_qt `Var si.si_pointer name.Clang_ast_t.ni_name false qt in + cast_expr decl_ref qt + | _ -> assert false in + + let build_cast_decl_ref_expr_from_parm p = + match p with + | ParmVarDecl(di, name, qt, _) -> + let decl_ref = make_decl_ref_qt `Var di.di_pointer name.Clang_ast_t.ni_name false qt in + cast_expr decl_ref qt + | _ -> assert false in + + let make_block_decl be = + match be with + | BlockExpr(bsi, _, bei, _) -> + let di = { empty_decl_info with Clang_ast_t.di_pointer = Ast_utils.get_fresh_pointer () } in + let vdi = { empty_var_decl_info with vdi_init_expr = Some (be) } in + let var_decl = VarDecl(di, make_name_decl block_name, bei.Clang_ast_t.ei_qual_type, vdi) in + DeclStmt(bsi, [be], [var_decl]), [(block_name, di.Clang_ast_t.di_pointer, bei.Clang_ast_t.ei_qual_type)] + | _ -> assert false in + + let make_block_call block_qt object_cast idx_cast stop_cast = + let decl_ref = make_decl_ref_invalid `Var block_name false block_qt in + let fun_cast = cast_expr decl_ref block_qt in + let ei_call = make_expr_info (create_void_type ()) in + CallExpr((fresh_stmt_info stmt_info), [fun_cast; object_cast; idx_cast; stop_cast], ei_call) in + + (* build statement "if (stop) break;" *) + let build_if_stop stop_cast = + let bool_qt = create_BOOL_type () in + let ei = make_expr_info bool_qt in + let unary_op = UnaryOperator((fresh_stmt_info stmt_info), [stop_cast], ei, { uoi_kind = `Deref; uoi_is_postfix = true }) in + let cond = create_implicit_cast_expr (fresh_stmt_info stmt_info) [unary_op] bool_qt `LValueToRValue in + let break_stmt = BreakStmt((fresh_stmt_info stmt_info),[]) in + IfStmt((fresh_stmt_info stmt_info), [dummy_stmt (); cond; break_stmt; dummy_stmt ()]) in + + let translate params array_cast_decl_ref_exp block_decl block_qt = + match params with + | [pobj; pidx; pstop] -> + let objects_decl, op = objects_array_DeclStmt array_cast_decl_ref_exp in + let decl_stop = build_stop pstop in + let assign_stop = stop_equal_no pstop in + let objects = make_object_cast_decl_ref_expr objects_decl in + let idx_decl_stmt, idx_decl_ref_exp, idx_cast, qt_idx = build_idx_decl pidx in + let guard = bin_op pidx objects in + let incr = un_op idx_decl_ref_exp qt_idx in + let obj_assignment = build_object_DeclStmt pobj objects idx_cast qt_idx in + let object_cast = build_cast_decl_ref_expr_from_parm pobj in + let stop_cast = build_cast_decl_ref_expr_from_parm pstop in + let call_block = make_block_call block_qt object_cast idx_cast stop_cast in + let if_stop = build_if_stop stop_cast in + let free_stop = free_stop pstop in + [ objects_decl; block_decl; decl_stop; assign_stop; + ForStmt(stmt_info, [idx_decl_stmt; dummy_stmt (); guard; incr; + CompoundStmt(stmt_info, [obj_assignment; call_block; if_stop])]); free_stop], op + | _ -> assert false in + + match stmt_list with + | [s; BlockExpr(_, _, bei, BlockDecl(_, _, _, bdi)) as be] -> + let block_decl, bv = make_block_decl be in + let vars_to_register = get_name_pointers bdi.Clang_ast_t.bdi_parameters in + let translated_stmt, op = translate bdi.Clang_ast_t.bdi_parameters s block_decl bei.Clang_ast_t.ei_qual_type in + CompoundStmt(stmt_info, translated_stmt), vars_to_register@op@bv + | _ -> (* When it is not the method we expect with only one parameter, we don't translate *) + Printing.log_out "WARNING: Block Enumeration called at %s not translated." (Clang_ast_j.string_of_stmt_info stmt_info); + CompoundStmt(stmt_info, stmt_list), [] + (* We translate the logical negation of an integer with a conditional*) (* !x <=> x?0:1 *) let trans_negation_with_conditional stmt_info expr_info stmt_list = @@ -367,7 +647,7 @@ let create_call stmt_info decl_pointer function_name qt parameters = Clang_ast_t.ei_object_kind = `Ordinary } in let expr_info_dre = make_expr_info_with_objc_kind qt `Ordinary in - let decl_ref = make_general_decl_ref `Function decl_pointer function_name false qt in + let decl_ref = make_decl_ref_qt `Function decl_pointer function_name false qt in let decl_ref_info = make_decl_ref_expr_info decl_ref in let decl_ref_exp = DeclRefExpr(stmt_info, [], expr_info_dre, decl_ref_info) in CallExpr(stmt_info, decl_ref_exp:: parameters, expr_info_call) @@ -376,7 +656,7 @@ let create_assume_not_null_call decl_info var_name var_type = let stmt_info = stmt_info_with_fresh_pointer (make_stmt_info decl_info) in let boi = { Clang_ast_t.boi_kind = `NE } in let decl_ptr = Ast_utils.get_invalid_pointer () in - let decl_ref = make_general_decl_ref `Var decl_ptr var_name false var_type in + let decl_ref = make_decl_ref_qt `Var decl_ptr var_name false var_type in let stmt_info_var = dummy_stmt_info () in let decl_ref_info = make_decl_ref_expr_info decl_ref in let var_decl_ref = DeclRefExpr(stmt_info_var, [], (make_expr_info var_type), decl_ref_info) in @@ -389,6 +669,6 @@ let create_assume_not_null_call decl_info var_name var_type = let cast_info_call = { cei_cast_kind = `LValueToRValue; cei_base_path = [] } in let decl_ref_exp_cast = ImplicitCastExpr(stmt_info, [var_decl_ref], expr_info, cast_info_call) in let null_expr = create_integer_literal stmt_info "0" in - let bin_op = make_binary_stmt decl_ref_exp_cast null_expr stmt_info (make_expr_info var_type) boi in + let bin_op = make_binary_stmt decl_ref_exp_cast null_expr stmt_info (make_lvalue_obc_prop_expr_info var_type) boi in let parameters = [bin_op] in create_call stmt_info var_decl_ptr (Procname.to_string SymExec.ModelBuiltins.__infer_assume) (create_void_type ()) parameters diff --git a/infer/src/clang/ast_expressions.mli b/infer/src/clang/ast_expressions.mli index b7450a780..abc1e4e62 100644 --- a/infer/src/clang/ast_expressions.mli +++ b/infer/src/clang/ast_expressions.mli @@ -31,10 +31,12 @@ val make_stmt_info : decl_info -> stmt_info val make_method_decl_info : obj_c_method_decl_info -> stmt -> obj_c_method_decl_info -val make_general_decl_ref : decl_kind -> pointer -> string -> bool -> qual_type -> decl_ref +val make_decl_ref_qt : decl_kind -> pointer -> string -> bool -> qual_type -> decl_ref val make_decl_ref_expr_info : decl_ref -> decl_ref_expr_info +val make_general_expr_info : qual_type -> value_kind -> object_kind -> expr_info + val make_expr_info : qual_type -> expr_info val make_cast_expr : qual_type -> decl_info -> decl_ref_expr_info -> object_kind -> stmt @@ -63,6 +65,8 @@ val make_obj_c_message_expr_info_instance : string -> obj_c_message_expr_info val translate_dispatch_function : string -> stmt_info -> stmt list -> expr_info -> int -> stmt * qual_type +val translate_block_enumerate : string -> stmt_info -> stmt list -> expr_info -> stmt * (string * string* qual_type) list + (* We translate the logical negation of an integer with a conditional*) (* !x <=> x?0:1 *) val trans_negation_with_conditional : stmt_info -> expr_info -> stmt list -> stmt diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index d3a3ea0db..938906cb1 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -15,6 +15,8 @@ let testing_mode = ref false let array_with_objects_count_m = "arrayWithObjects:count:" +let object_at_indexed_subscript_m = "objectAtIndexedSubscript:" + let dict_with_objects_and_keys_m = "dictionaryWithObjectsAndKeys:" let string_with_utf8_m = "stringWithUTF8String:" @@ -35,6 +37,8 @@ let alloc = "alloc" let malloc = "malloc" +let free = "free" + let static = "static" let source_file : string option ref = ref None @@ -131,3 +135,13 @@ let handleFailureInFunction = "handleFailureInFunction:file:lineNumber:descripti let fbAssertWithSignalAndLogFunctionHelper = "FBAssertWithSignalAndLogFunctionHelper" let nonnull_attribute = "__nonnull" + +let pseudo_object_type = "" + +let count = "count" + +let objects = "objects" + +let ns_array_ptr = "NSArray *" + +let enumerateObjectsUsingBlock = "enumerateObjectsUsingBlock:" \ No newline at end of file diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 6c9c89db0..d6e967483 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -63,10 +63,14 @@ val alloc : string val malloc : string +val free : string + val static : string val array_with_objects_count_m : string +val object_at_indexed_subscript_m : string + val dict_with_objects_and_keys_m : string val emtpy_name_category : string @@ -128,3 +132,13 @@ val handleFailureInMethod : string val handleFailureInFunction : string val nonnull_attribute : string + +val pseudo_object_type : string + +val count : string + +val objects : string + +val ns_array_ptr : string + +val enumerateObjectsUsingBlock : string \ No newline at end of file diff --git a/infer/src/clang/cMethod_decl.ml b/infer/src/clang/cMethod_decl.ml index 4433a635d..9c7a965d5 100644 --- a/infer/src/clang/cMethod_decl.ml +++ b/infer/src/clang/cMethod_decl.ml @@ -112,7 +112,7 @@ struct let add_method tenv cg cfg class_decl_opt procname namespace instrs is_objc_method is_instance captured_vars is_anonym_block param_decls attributes = Printing.log_out - "\n\n>>---------- ADDING METHOD: '%s' ---------<<\n" (Procname.to_string procname); + "\n\n>>---------- ADDING METHOD: '%s' ---------<<\n@." (Procname.to_string procname); try (match Cfg.Procdesc.find_from_name cfg procname with | Some procdesc -> @@ -127,7 +127,7 @@ struct Cfg.Procdesc.append_locals procdesc local_vars; Cfg.Node.add_locals_ret_declaration start_node local_vars; Printing.log_out - "\n\n>>---------- Start translating the function: '%s' ---------<<" + "\n\n>>---------- Start translating body of function: '%s' ---------<<\n@." (Procname.to_string procname); let nonnull_assume_calls = add_assume_not_null_calls param_decls in let instrs' = instrs@nonnull_assume_calls attributes in @@ -191,7 +191,7 @@ struct let prop_methods = ObjcProperty_decl.make_getter_setter cfg curr_class decl_info property_impl_decl_info in list_iter (process_one_method_decl tenv cg cfg curr_class namespace) prop_methods - | EmptyDecl _ | ObjCIvarDecl _ -> () + | EmptyDecl _ | ObjCIvarDecl _ | ObjCPropertyDecl _ -> () | d -> Printing.log_err "\nWARNING: found Method Declaration '%s' skipped. NEED TO BE FIXED\n\n" (Ast_utils.string_of_decl d); () diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index bfe5e8b2b..4c0cbbe58 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -158,8 +158,7 @@ let create_local_procdesc cfg tenv ms fbody captured is_objc_inst_method = (* Captured variables for blocks are treated as parameters *) let formals = captured_str @formals in let source_range = CMethod_signature.ms_get_loc ms in - Printing.log_out - "\n\n>>------------------------- Start creating a new procdesc for function: '%s' ---------<<\n" pname; + Printing.log_out "\nCreating a new procdesc for function: '%s'\n@." pname; let loc_start = CLocation.get_sil_location_from_range source_range true in let loc_exit = CLocation.get_sil_location_from_range source_range false in let ret_type = get_return_type tenv ms in diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 25cb63792..6e3309512 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -143,8 +143,9 @@ struct (* At the end of block translation, we need to get the proirity back.*) (* the parameter f will be called with function instruction *) let exec_with_block_priority_exception f trans_state e stmt_info = - if (is_block_expr e) && (PriorityNode.own_priority_node trans_state.priority stmt_info) then - f { trans_state with priority = Free } e + if (is_block_expr e) && (PriorityNode.own_priority_node trans_state.priority stmt_info) then ( + Printing.log_out "Translating block expression by freeing the priority"; + f { trans_state with priority = Free } e) else f trans_state e (* This is the standard way of dealing with self:Class or a call [a class]. We translate it as sizeof() *) @@ -274,7 +275,8 @@ struct { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } let declRefExpr_trans trans_state stmt_info expr_info decl_ref_expr_info d = - Printing.log_out "Passing from DeclRefExpr '%s' \n" stmt_info.Clang_ast_t.si_pointer; + Printing.log_out "Passing from DeclRefExpr '%s' priority node free = '%s'\n@." stmt_info.Clang_ast_t.si_pointer + (string_of_bool (PriorityNode.is_priority_free trans_state)); let context = trans_state.context in let typ = CTypes_decl.qual_type_to_sil_type context.tenv expr_info.Clang_ast_t.ei_qual_type in let name = get_name_decl_ref_exp_info decl_ref_expr_info stmt_info in @@ -543,8 +545,7 @@ struct (Procname.to_string pn) <> CFrontend_config.builtin_object_size | _ -> true in let params_stmt = if should_translate_args then - CTrans_utils.assign_default_params params_stmt callee_pname_opt - else [] in + CTrans_utils.assign_default_params params_stmt callee_pname_opt else [] in let res_trans_par = let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) params_stmt in let rt = collect_res_trans (res_trans_callee :: l) in @@ -600,7 +601,8 @@ struct | _ -> assert false) (* by construction of red_id, we cannot be in this case *) and objCMessageExpr_trans trans_state si obj_c_message_expr_info stmt_list expr_info = - Printing.log_out "Passing from ObjMessageExpr '%s'.\n" si.Clang_ast_t.si_pointer; + Printing.log_out "Passing from ObjMessageExpr '%s' priority node free ='%s'.\n@." si.Clang_ast_t.si_pointer + (string_of_bool (PriorityNode.is_priority_free trans_state)); let context = trans_state.context in let parent_line_number = trans_state.parent_line_number in let sil_loc = get_sil_location si parent_line_number context in @@ -656,8 +658,8 @@ struct instrs = res_trans_par.instrs@instr_block_param@[stmt_call]; exps =[] } in - let res_trans_to_parent = - PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp in + let res_trans_to_parent = ( + PriorityNode.compute_results_to_parent trans_state_pri sil_loc nname si res_trans_tmp) in (match ret_id with | [] -> { res_trans_to_parent with exps = [] } | [ret_id'] -> { res_trans_to_parent with exps = [(Sil.Var ret_id', method_type)] } @@ -680,6 +682,29 @@ struct list_iter (fun n -> Cfg.Node.append_instrs_temps n [Sil.Nullify(pvar, loc, true)] []) preds; res_state + and block_enumeration_trans trans_state stmt_info stmt_list ei = + let declare_nullify_vars loc res_state roots preds (pvar, typ) = + (* Add declare locals to the first node *) + list_iter (fun n -> Cfg.Node.prepend_instrs_temps n [Sil.Declare_locals([(pvar, typ)], loc)] []) roots; + (* Add nullify of the temp block var to the last node (predecessor or the successor nodes)*) + list_iter (fun n -> Cfg.Node.append_instrs_temps n [Sil.Nullify(pvar, loc, true)] []) preds in + + Printing.log_out "\n Call to a block enumeration function treated as special case...\n@."; + let procname = Cfg.Procdesc.get_proc_name trans_state.context.procdesc in + let pvar = CFrontend_utils.General_utils.get_next_block_pvar procname in + let transformed_stmt, vars_to_register = + Ast_expressions.translate_block_enumerate (Sil.pvar_to_string pvar) stmt_info stmt_list ei in + let pvars_types = list_map (fun (v, pointer, qt) -> + let pvar = Sil.mk_pvar (Mangled.from_string v) procname in + let typ = CTypes_decl.qual_type_to_sil_type trans_state.context.tenv qt in + CContext.LocalVars.add_pointer_var pointer pvar trans_state.context; + (pvar, typ)) vars_to_register in + let loc = get_sil_location stmt_info trans_state.parent_line_number trans_state.context in + let res_state = instruction trans_state transformed_stmt in + let preds = list_flatten (list_map (fun n -> Cfg.Node.get_preds n) trans_state.succ_nodes) in + list_iter (declare_nullify_vars loc res_state res_state.root_nodes preds) pvars_types; + res_state + and compoundStmt_trans trans_state stmt_info stmt_list = Printing.log_out "Passing from CompoundStmt '%s'.\n" stmt_info.Clang_ast_t.si_pointer; let line_number = get_line stmt_info trans_state.parent_line_number in @@ -1282,7 +1307,6 @@ struct let node = list_hd next_node in Cfg.Node.append_instrs_temps node instrs ids; list_iter (fun n -> Cfg.Node.set_succs_exn n [node] []) leaf_nodes; - let root_nodes = if (list_length root_nodes) = 0 then next_node else root_nodes in { root_nodes = root_nodes; leaf_nodes =[]; ids = ids; instrs = instrs; exps = [(Sil.Lvar pvar, ie_typ)]} ) else { root_nodes = root_nodes; leaf_nodes =[]; ids = ids; instrs = instrs; exps =[(Sil.Lvar pvar, ie_typ)]}) in @@ -1329,7 +1353,8 @@ struct (* For OpaqueValueExpr we return the translation generated from its source expression*) and opaqueValueExpr_trans trans_state stmt_info opaque_value_expr_info = - Printing.log_out "Passing from OpaqueValueExpr '%s' \n" stmt_info.Clang_ast_t.si_pointer; + Printing.log_out "Passing from OpaqueValueExpr '%s' priority node free ='%s'\n@." stmt_info.Clang_ast_t.si_pointer + (string_of_bool (PriorityNode.is_priority_free trans_state)); (match opaque_value_expr_info.Clang_ast_t.ovei_source_expr with | Some stmt -> instruction trans_state stmt | _ -> assert false) @@ -1351,7 +1376,8 @@ struct and pseudoObjectExpr_trans trans_state stmt_info stmt_list = let line_number = get_line stmt_info trans_state.parent_line_number in let trans_state' = { trans_state with parent_line_number = line_number } in - Printing.log_out "Passing from PseudoObjectExpr '%s' \n" stmt_info.Clang_ast_t.si_pointer; + Printing.log_out "Passing from PseudoObjectExpr '%s' priority node free ='%s' \n" stmt_info.Clang_ast_t.si_pointer + (string_of_bool (PriorityNode.is_priority_free trans_state)); let rec do_semantic_elements el = (match el with | OpaqueValueExpr _ :: el' -> do_semantic_elements el' @@ -1366,7 +1392,8 @@ struct and cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_expr_info is_objc_bridged = let context = trans_state.context in let pln = trans_state.parent_line_number in - Printing.log_out "Passing from CastExpr '%s' \n" stmt_info.Clang_ast_t.si_pointer; + Printing.log_out "Passing from CastExpr '%s' priority node free = '%s' \n" stmt_info.Clang_ast_t.si_pointer + (string_of_bool (PriorityNode.is_priority_free trans_state)); let sil_loc = get_sil_location stmt_info pln context in let stmt = extract_stmt_from_singleton stmt_list "WARNING: In CastExpr There must be only one stmt defining the expression to be cast.\n" in @@ -1652,7 +1679,10 @@ struct callExpr_trans trans_state stmt_info stmt_list ei) | ObjCMessageExpr(stmt_info, stmt_list, expr_info, obj_c_message_expr_info) -> - objCMessageExpr_trans trans_state stmt_info obj_c_message_expr_info stmt_list expr_info + if is_block_enumerate_function obj_c_message_expr_info then + block_enumeration_trans trans_state stmt_info stmt_list expr_info + else + objCMessageExpr_trans trans_state stmt_info obj_c_message_expr_info stmt_list expr_info | CompoundStmt (stmt_info, stmt_list) -> (* No node for this statement. We just collect its statement list*) diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index e6ec89187..c95868d3f 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -194,8 +194,12 @@ struct let try_claim_priority_node trans_state stmt_info = match trans_state.priority with - | Free -> { trans_state with priority = Busy stmt_info.Clang_ast_t.si_pointer } - | _ -> trans_state + | Free -> + Printing.log_out "Priority is free. Locking priority node in %s\n@." stmt_info.Clang_ast_t.si_pointer; + { trans_state with priority = Busy stmt_info.Clang_ast_t.si_pointer } + | _ -> + Printing.log_out "Priority busy in %s. No claim possible\n@." stmt_info.Clang_ast_t.si_pointer; + trans_state let is_priority_free trans_state = match trans_state.priority with @@ -683,3 +687,6 @@ let assign_default_params params_stmt callee_pname_opt = Printing.log_err "Param count doesn't match %s\n" (Procname.to_string callee_pname); params_stmt | Not_found -> params_stmt + +let is_block_enumerate_function mei = + mei.Clang_ast_t.omei_selector = CFrontend_config.enumerateObjectsUsingBlock diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 7317a2d0d..fdeff1061 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -202,3 +202,5 @@ val is_logical_negation_of_int : Sil.tenv -> Clang_ast_t.expr_info -> Clang_ast_ val is_dispatch_function : Clang_ast_t.stmt list -> int option val assign_default_params : Clang_ast_t.stmt list -> Procname.t option -> Clang_ast_t.stmt list + +val is_block_enumerate_function : Clang_ast_t.obj_c_message_expr_info -> bool diff --git a/infer/src/clang/cTypes_decl.ml b/infer/src/clang/cTypes_decl.ml index 28ecb2a13..aded4bd96 100644 --- a/infer/src/clang/cTypes_decl.ml +++ b/infer/src/clang/cTypes_decl.ml @@ -97,7 +97,7 @@ let string_type_to_sil_type tenv s = let t = CTypes_parser.parse (Ast_lexer.token) lexbuf in Printing.log_out " ...Parsed. Translated with sil TYPE '%a'@." (Sil.pp_typ_full pe_text) t; - t + t with Parsing.Parse_error -> ( Printing.log_stats "\nXXXXXXX PARSE ERROR for string '%s'. RETURNING Void.TODO@.@." s; @@ -116,7 +116,6 @@ let opt_type_to_sil_type tenv opt_type = | `Type(s) -> qual_type_to_sil_type_no_expansions tenv (Ast_expressions.create_qual_type s) | `NoType -> Sil.Tvoid - let parse_func_type name func_type = try let lexbuf = Lexing.from_string func_type in @@ -202,7 +201,7 @@ and do_record_declaration tenv namespace decl_info name opt_type decl_list decl_ Printing.log_out "ADDING: RecordDecl for '%s'" name; Printing.log_out " pointer= '%s'\n" decl_info.Clang_ast_t.di_pointer; if not record_decl_info.Clang_ast_t.rdi_is_complete_definition then - Printing.log_err " ...Warning, definition incomplete. The full definition will probably be later \n"; + Printing.log_err " ...Warning, definition incomplete. The full definition will probably be later \n@."; let typ = get_declaration_type tenv namespace decl_info name opt_type decl_list decl_context_info record_decl_info in let typ = expand_structured_type tenv typ in add_struct_to_tenv tenv typ diff --git a/infer/src/clang/cVar_decl.ml b/infer/src/clang/cVar_decl.ml index 699e28247..221a05e87 100644 --- a/infer/src/clang/cVar_decl.ml +++ b/infer/src/clang/cVar_decl.ml @@ -94,7 +94,7 @@ let lookup_var stmt_info context pointer var_name kind = try lookup_var_static_globals context var_name with Not_found -> - (Printing.log_out "Looking on later-defined decls for '%s'\n" var_name; + (Printing.log_out "Looking on later-defined decls for '%s' with pointer '%s' \n" var_name stmt_info.Clang_ast_t.si_pointer; let decl_list = !CFrontend_config.global_translation_unit_decls in lookup_ahead_for_vardecl context pointer var_name kind decl_list ) diff --git a/infer/src/clang/objcProperty_decl.ml b/infer/src/clang/objcProperty_decl.ml index 5d7a3b088..9c60a91d1 100644 --- a/infer/src/clang/objcProperty_decl.ml +++ b/infer/src/clang/objcProperty_decl.ml @@ -306,7 +306,7 @@ let make_getter_setter cfg curr_class decl_info property_impl_decl_info = | _ -> false) in let decl_ptr = Ast_utils.get_invalid_pointer () in let drti_decl_ref' = - Ast_expressions.make_general_decl_ref (`ParmVar) decl_ptr param_name is_hidden qt_param in + Ast_expressions.make_decl_ref_qt (`ParmVar) decl_ptr param_name is_hidden qt_param in let decl_ref_expr_info' = Ast_expressions.make_decl_ref_expr_info drti_decl_ref' in let expr_info = Ast_expressions.make_expr_info qt_param in let stmt_info = Ast_expressions.make_stmt_info dummy_info in diff --git a/infer/tests/codetoanalyze/objc/frontend/block/block-it.dot b/infer/tests/codetoanalyze/objc/frontend/block/block-it.dot new file mode 100644 index 000000000..a8cda156d --- /dev/null +++ b/infer/tests/codetoanalyze/objc/frontend/block/block-it.dot @@ -0,0 +1,212 @@ +digraph iCFG { +52 [label="52: DeclStmt \n n$47=_fun___objc_alloc_no_fail(sizeof(class NSArray ):class NSArray *) [line 30]\n n$45=_fun_NSArray_init(n$47:class NSArray *) virtual [line 30]\n *&a:class NSArray *=n$45 [line 30]\n REMOVE_TEMPS(n$45,n$47); [line 30]\n " shape="box"] + + + 52 -> 51 ; +51 [label="51: DeclStmt \n n$44=*&a:class NSArray * [line 32]\n *&objects:class NSArray *=n$44 [line 32]\n REMOVE_TEMPS(n$44); [line 32]\n NULLIFY(&a,false); [line 32]\n " shape="box"] + + + 51 -> 44 ; +50 [label="50: BinaryOperatorStmt: Assign \n NULLIFY(&ShouldStop,false); [line 40]\n n$42=*&stop:BOOL * [line 40]\n *n$42:_Bool =1 [line 40]\n REMOVE_TEMPS(n$42); [line 40]\n NULLIFY(&stop,false); [line 40]\n APPLY_ABSTRACTION; [line 40]\n " shape="box"] + + + 50 -> 47 ; +49 [label="49: Prune (false branch) \n n$41=*&ShouldStop:int [line 39]\n PRUNE((n$41 == 0), false); [line 39]\n REMOVE_TEMPS(n$41); [line 39]\n APPLY_ABSTRACTION; [line 39]\n " shape="invhouse"] + + + 49 -> 47 ; +48 [label="48: Prune (true branch) \n n$41=*&ShouldStop:int [line 39]\n PRUNE((n$41 != 0), true); [line 39]\n REMOVE_TEMPS(n$41); [line 39]\n " shape="invhouse"] + + + 48 -> 50 ; +47 [label="47: + \n NULLIFY(&ShouldStop,false); [line 39]\n NULLIFY(&stop,false); [line 39]\n " ] + + + 47 -> 46 ; +46 [label="46: Exit __objc_anonymous_block_MyBlock_array_trans______2 \n " color=yellow style=filled] + + +45 [label="45: Start __objc_anonymous_block_MyBlock_array_trans______2\nFormals: object:struct objc_object * idx:unsigned long stop:BOOL *\nLocals: ShouldStop:int \n DECLARE_LOCALS(&return,&ShouldStop); [line 35]\n NULLIFY(&idx,false); [line 35]\n NULLIFY(&object,false); [line 35]\n " color=yellow style=filled] + + + 45 -> 48 ; + 45 -> 49 ; +44 [label="44: DeclStmt \n DECLARE_LOCALS(&__objc_anonymous_block_MyBlock_array_trans______2); [line 35]\n n$43=_fun___objc_alloc_no_fail(sizeof(class __objc_anonymous_block_MyBlock_array_trans______2 ):class __objc_anonymous_block_MyBlock_array_trans______2 *) [line 35]\n *&__objc_anonymous_block_MyBlock_array_trans______2:class __objc_anonymous_block_MyBlock_array_trans______2 =n$43 [line 35]\n *&enumerateObjectsUsingBlock:_fn_ (*)=(_fun___objc_anonymous_block_MyBlock_array_trans______2) [line 35]\n REMOVE_TEMPS(n$43); [line 35]\n " shape="box"] + + + 44 -> 43 ; +43 [label="43: DeclStmt \n n$40=_fun_malloc_no_fail(sizeof(_Bool ):_Bool ) [line 43]\n *&stop:BOOL *=n$40 [line 43]\n REMOVE_TEMPS(n$40); [line 43]\n " shape="box"] + + + 43 -> 42 ; +42 [label="42: BinaryOperatorStmt: Assign \n n$39=*&stop:BOOL * [line 44]\n *n$39:_Bool =0 [line 44]\n REMOVE_TEMPS(n$39); [line 44]\n " shape="box"] + + + 42 -> 31 ; +41 [label="41: DeclStmt \n n$37=*&objects:class NSArray * [line 49]\n n$38=*&idx:unsigned long [line 49]\n n$36=_fun_NSArray_objectAtIndexedSubscript:(n$37:class NSArray *,n$38:unsigned long ) virtual [line 49]\n *&object:struct objc_object *=n$36 [line 49]\n REMOVE_TEMPS(n$36,n$37,n$38); [line 49]\n " shape="box"] + + + 41 -> 40 ; +40 [label="40: Call n$32 \n n$32=*&enumerateObjectsUsingBlock:_fn_ (*) [line 50]\n n$33=*&object:struct objc_object * [line 50]\n n$34=*&idx:unsigned long [line 50]\n n$35=*&stop:BOOL * [line 50]\n n$32(n$33:struct objc_object *,n$34:unsigned long ,n$35:BOOL *) [line 50]\n REMOVE_TEMPS(n$32,n$33,n$34,n$35); [line 50]\n NULLIFY(&object,false); [line 50]\n " shape="box"] + + + 40 -> 37 ; +39 [label="39: Prune (false branch) \n PRUNE(((n$31 == 1) == 0), false); [line 51]\n REMOVE_TEMPS(n$30,n$31); [line 51]\n " shape="invhouse"] + + + 39 -> 36 ; +38 [label="38: Prune (true branch) \n PRUNE(((n$31 == 1) != 0), true); [line 51]\n REMOVE_TEMPS(n$30,n$31); [line 51]\n APPLY_ABSTRACTION; [line 51]\n " shape="invhouse"] + + + 38 -> 29 ; +37 [label="37: BinaryOperatorStmt: EQ \n n$30=*&stop:BOOL * [line 51]\n n$31=*n$30:_Bool [line 51]\n " shape="box"] + + + 37 -> 38 ; + 37 -> 39 ; +36 [label="36: + \n " ] + + + 36 -> 32 ; +35 [label="35: Prune (false branch) \n PRUNE(((n$27 < n$28) == 0), false); [line 46]\n REMOVE_TEMPS(n$27,n$28,n$29); [line 46]\n APPLY_ABSTRACTION; [line 46]\n " shape="invhouse"] + + + 35 -> 29 ; +34 [label="34: Prune (true branch) \n PRUNE(((n$27 < n$28) != 0), true); [line 46]\n REMOVE_TEMPS(n$27,n$28,n$29); [line 46]\n " shape="invhouse"] + + + 34 -> 41 ; +33 [label="33: BinaryOperatorStmt: LT \n n$27=*&idx:unsigned long [line 46]\n n$29=*&objects:class NSArray * [line 46]\n n$28=_fun_NSArray_count(n$29:class NSArray *) virtual [line 46]\n " shape="box"] + + + 33 -> 34 ; + 33 -> 35 ; +32 [label="32: UnaryOperator \n n$26=*&idx:unsigned long [line 46]\n *&idx:unsigned long =(n$26 + 1) [line 46]\n REMOVE_TEMPS(n$26); [line 46]\n APPLY_ABSTRACTION; [line 46]\n " shape="box"] + + + 32 -> 30 ; +31 [label="31: DeclStmt \n *&idx:unsigned long =0 [line 46]\n APPLY_ABSTRACTION; [line 46]\n " shape="box"] + + + 31 -> 30 ; +30 [label="30: + \n " ] + + + 30 -> 33 ; +29 [label="29: Call _fun_free \n NULLIFY(&enumerateObjectsUsingBlock,false); [line 53]\n NULLIFY(&idx,false); [line 53]\n NULLIFY(&objects,false); [line 53]\n n$25=*&stop:BOOL * [line 53]\n _fun_free(n$25:void *) [line 53]\n REMOVE_TEMPS(n$25); [line 53]\n NULLIFY(&__objc_anonymous_block_MyBlock_array_trans______2,true); [line 53]\n NULLIFY(&stop,false); [line 53]\n APPLY_ABSTRACTION; [line 53]\n " shape="box"] + + + 29 -> 28 ; +28 [label="28: Exit MyBlock_array_trans \n " color=yellow style=filled] + + +27 [label="27: Start MyBlock_array_trans\nFormals: self:class MyBlock *\nLocals: a:class NSArray * objects:class NSArray * enumerateObjectsUsingBlock:_fn_ (*) stop:BOOL * idx:unsigned long object:struct objc_object * \n DECLARE_LOCALS(&return,&a,&objects,&enumerateObjectsUsingBlock,&stop,&idx,&object); [line 28]\n NULLIFY(&a,false); [line 28]\n NULLIFY(&enumerateObjectsUsingBlock,false); [line 28]\n NULLIFY(&idx,false); [line 28]\n NULLIFY(&object,false); [line 28]\n NULLIFY(&objects,false); [line 28]\n NULLIFY(&self,false); [line 28]\n NULLIFY(&stop,false); [line 28]\n " color=yellow style=filled] + + + 27 -> 52 ; +26 [label="26: DeclStmt \n n$24=_fun___objc_alloc_no_fail(sizeof(class NSArray ):class NSArray *) [line 14]\n n$22=_fun_NSArray_init(n$24:class NSArray *) virtual [line 14]\n *&a:class NSArray *=n$22 [line 14]\n REMOVE_TEMPS(n$22,n$24); [line 14]\n " shape="box"] + + + 26 -> 25 ; +25 [label="25: DeclStmt \n DECLARE_LOCALS(&infer___objc_anonymous_block_MyBlock_array______1); [line 15]\n DECLARE_LOCALS(&objects); [line 15]\n DECLARE_LOCALS(&stop); [line 15]\n DECLARE_LOCALS(&idx); [line 15]\n DECLARE_LOCALS(&object); [line 15]\n n$21=*&a:class NSArray * [line 15]\n *&objects:class NSArray *=n$21 [line 15]\n REMOVE_TEMPS(n$21); [line 15]\n NULLIFY(&a,false); [line 15]\n " shape="box"] + + + 25 -> 18 ; +24 [label="24: BinaryOperatorStmt: Assign \n NULLIFY(&ShouldStop,false); [line 22]\n n$19=*&stop:BOOL * [line 22]\n *n$19:_Bool =1 [line 22]\n REMOVE_TEMPS(n$19); [line 22]\n NULLIFY(&stop,false); [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] + + + 24 -> 21 ; +23 [label="23: Prune (false branch) \n n$18=*&ShouldStop:int [line 20]\n PRUNE((n$18 == 0), false); [line 20]\n REMOVE_TEMPS(n$18); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="invhouse"] + + + 23 -> 21 ; +22 [label="22: Prune (true branch) \n n$18=*&ShouldStop:int [line 20]\n PRUNE((n$18 != 0), true); [line 20]\n REMOVE_TEMPS(n$18); [line 20]\n " shape="invhouse"] + + + 22 -> 24 ; +21 [label="21: + \n NULLIFY(&ShouldStop,false); [line 20]\n NULLIFY(&stop,false); [line 20]\n " ] + + + 21 -> 20 ; +20 [label="20: Exit __objc_anonymous_block_MyBlock_array______1 \n " color=yellow style=filled] + + +19 [label="19: Start __objc_anonymous_block_MyBlock_array______1\nFormals: object:struct objc_object * idx:unsigned long stop:BOOL *\nLocals: ShouldStop:int \n DECLARE_LOCALS(&return,&ShouldStop); [line 15]\n NULLIFY(&idx,false); [line 15]\n NULLIFY(&object,false); [line 15]\n " color=yellow style=filled] + + + 19 -> 22 ; + 19 -> 23 ; +18 [label="18: DeclStmt \n DECLARE_LOCALS(&__objc_anonymous_block_MyBlock_array______1); [line 15]\n n$20=_fun___objc_alloc_no_fail(sizeof(class __objc_anonymous_block_MyBlock_array______1 ):class __objc_anonymous_block_MyBlock_array______1 *) [line 15]\n *&__objc_anonymous_block_MyBlock_array______1:class __objc_anonymous_block_MyBlock_array______1 =n$20 [line 15]\n *&infer___objc_anonymous_block_MyBlock_array______1:_fn_ (*)=(_fun___objc_anonymous_block_MyBlock_array______1) [line 15]\n REMOVE_TEMPS(n$20); [line 15]\n " shape="box"] + + + 18 -> 17 ; +17 [label="17: DeclStmt \n n$17=_fun_malloc_no_fail(sizeof(signed char ):signed char ) [line 15]\n *&stop:BOOL *=n$17 [line 15]\n REMOVE_TEMPS(n$17); [line 15]\n " shape="box"] + + + 17 -> 16 ; +16 [label="16: BinaryOperatorStmt: Assign \n n$16=*&stop:BOOL * [line 15]\n *n$16:void =0 [line 15]\n REMOVE_TEMPS(n$16); [line 15]\n " shape="box"] + + + 16 -> 5 ; +15 [label="15: DeclStmt \n n$14=*&objects:class NSArray * [line 15]\n n$15=*&idx:unsigned long [line 15]\n n$13=_fun_NSArray_objectAtIndexedSubscript:(n$14:class NSArray *,n$15:unsigned long ) virtual [line 15]\n *&object:struct objc_object *=n$13 [line 15]\n REMOVE_TEMPS(n$13,n$14,n$15); [line 15]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Call n$8 \n n$8=*&infer___objc_anonymous_block_MyBlock_array______1:_fn_ (*) [line 15]\n n$9=*&object:struct objc_object * [line 15]\n n$10=*&idx:unsigned long [line 15]\n n$11=*&stop:BOOL * [line 15]\n n$12=n$8(n$9:struct objc_object *,n$10:unsigned long ,n$11:BOOL *) [line 15]\n REMOVE_TEMPS(n$8,n$9,n$10,n$11,n$12); [line 15]\n " shape="box"] + + + 14 -> 11 ; +13 [label="13: Prune (false branch) \n n$7=*n$6:signed char [line 15]\n PRUNE((n$7 == 0), false); [line 15]\n REMOVE_TEMPS(n$6,n$7); [line 15]\n " shape="invhouse"] + + + 13 -> 10 ; +12 [label="12: Prune (true branch) \n n$7=*n$6:signed char [line 15]\n PRUNE((n$7 != 0), true); [line 15]\n REMOVE_TEMPS(n$6,n$7); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="invhouse"] + + + 12 -> 3 ; +11 [label="11: UnaryOperator \n n$6=*&stop:BOOL * [line 15]\n " shape="box"] + + + 11 -> 12 ; + 11 -> 13 ; +10 [label="10: + \n " ] + + + 10 -> 6 ; +9 [label="9: Prune (false branch) \n PRUNE(((n$3 < n$4) == 0), false); [line 15]\n REMOVE_TEMPS(n$3,n$4,n$5); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="invhouse"] + + + 9 -> 3 ; +8 [label="8: Prune (true branch) \n PRUNE(((n$3 < n$4) != 0), true); [line 15]\n REMOVE_TEMPS(n$3,n$4,n$5); [line 15]\n " shape="invhouse"] + + + 8 -> 15 ; +7 [label="7: BinaryOperatorStmt: LT \n n$3=*&idx:unsigned long [line 15]\n n$5=*&objects:class NSArray * [line 15]\n n$4=_fun_NSArray_count(n$5:class NSArray *) virtual [line 15]\n " shape="box"] + + + 7 -> 8 ; + 7 -> 9 ; +6 [label="6: UnaryOperator \n n$2=*&idx:unsigned long [line 15]\n *&idx:unsigned long =(n$2 + 1) [line 15]\n REMOVE_TEMPS(n$2); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] + + + 6 -> 4 ; +5 [label="5: DeclStmt \n *&idx:unsigned long =0 [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: + \n " ] + + + 4 -> 7 ; +3 [label="3: Call _fun_free \n n$0=*&stop:BOOL * [line 15]\n n$1=_fun_free(n$0:void *) [line 15]\n NULLIFY(&object,true); [line 15]\n NULLIFY(&idx,true); [line 15]\n NULLIFY(&stop,true); [line 15]\n NULLIFY(&objects,true); [line 15]\n REMOVE_TEMPS(n$0,n$1); [line 15]\n NULLIFY(&__objc_anonymous_block_MyBlock_array______1,true); [line 15]\n NULLIFY(&infer___objc_anonymous_block_MyBlock_array______1,true); [line 15]\n APPLY_ABSTRACTION; [line 15]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit MyBlock_array \n " color=yellow style=filled] + + +1 [label="1: Start MyBlock_array\nFormals: self:class MyBlock *\nLocals: a:class NSArray * \n DECLARE_LOCALS(&return,&a); [line 12]\n NULLIFY(&a,false); [line 12]\n NULLIFY(&self,false); [line 12]\n " color=yellow style=filled] + + + 1 -> 26 ; +} diff --git a/infer/tests/codetoanalyze/objc/frontend/block/block-it.m b/infer/tests/codetoanalyze/objc/frontend/block/block-it.m new file mode 100644 index 000000000..a037c21f0 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/frontend/block/block-it.m @@ -0,0 +1,58 @@ + +#import + +@interface MyBlock : NSObject + +@end + +@implementation MyBlock + + + +-(void) array { + + NSArray *a = [[NSArray alloc] init]; + [a enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) { + int ShouldStop; + /* + ... do something here... + */ + if (ShouldStop + ) { + *stop= YES; + }; + }]; +} + + +-(void) array_trans { + +NSArray *a = [[NSArray alloc] init]; + + NSArray *objects = a; + + // The call to the iterator in the above function is translated as: + void (^enumerateObjectsUsingBlock)(id, NSUInteger, BOOL *)= ^(id object, NSUInteger idx, BOOL* stop) { + + int ShouldStop; + + if (ShouldStop) { + *stop= YES; + }; + }; + BOOL *stop = malloc(sizeof(BOOL)); + *stop = NO; + + for (NSUInteger idx=0; idx inferCmd = + InferRunner.createiOSInferCommandFrontend(folder, block_src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + block_src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(block_dotty)); + } + + }