From d3f20dcacaf3ad2d3ecc10628185cb9db312f303 Mon Sep 17 00:00:00 2001 From: Dulma Rodriguez Date: Wed, 24 Jun 2015 11:36:10 -0100 Subject: [PATCH] [clang] Fix translation of fast loops --- infer/src/clang/ast_expressions.ml | 58 +++++++------- infer/src/clang/ast_expressions.mli | 10 +-- infer/src/clang/cFrontend_config.ml | 2 + infer/src/clang/cFrontend_config.mli | 2 + infer/src/clang/cTrans.ml | 21 +++-- infer/src/clang/objcProperty_decl.ml | 6 +- .../objc/frontend/boxing/array.dot | 16 ++-- .../fast_enumeration/Fast_enumeration.dot | 78 +++++++++++++++++++ .../fast_enumeration/Fast_enumeration.m | 25 ++++++ .../frontend/objc/FastEnumerationTest.java | 47 +++++++++++ 10 files changed, 212 insertions(+), 53 deletions(-) create mode 100644 infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot create mode 100644 infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.m create mode 100644 infer/tests/frontend/objc/FastEnumerationTest.java diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index 202fbe5a8..37371569a 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -147,10 +147,16 @@ let make_decl_ref_exp stmt_info expr_info drei = } in DeclRefExpr(stmt_info, [], expr_info, drei) -let make_obj_c_message_expr_info sel typ = +let make_obj_c_message_expr_info_instance sel = { Clang_ast_t.omei_selector = sel; - Clang_ast_t.omei_receiver_kind = `Class (create_qual_type typ) + Clang_ast_t.omei_receiver_kind = `Instance + } + +let make_obj_c_message_expr_info_class selector qt = + { + omei_selector = selector; + omei_receiver_kind = `Class (create_qual_type qt); } let make_general_decl_ref k name is_hidden qt = { @@ -181,17 +187,6 @@ let make_obj_c_ivar_ref_expr_info k n qt = { Clang_ast_t.ovrei_is_free_ivar = true; } -let make_nondet_exp stmt_info = - let drei = { - Clang_ast_t.drti_decl_ref = Some (make_decl_ref "__INFER_NON_DET"); - Clang_ast_t.drti_found_decl_ref = None; } in - let qt = create_int_type () in - let ei = make_expr_info qt `Ordinary in - let e = make_decl_ref_exp stmt_info ei drei in - let cast = create_implicit_cast_expr stmt_info [e] qt `LValueToRValue in - let zero = create_integer_literal stmt_info "0" in - BinaryOperator(stmt_info, [cast; zero], ei, { Clang_ast_t.boi_kind = `GT } ) - (* Build an AST cast expression of a decl_ref_expr *) let make_cast_expr qt di decl_ref_expr_info objc_kind = let expr_info = make_expr_info qt objc_kind in @@ -232,7 +227,7 @@ let make_objc_ivar_decl decl_info qt property_impl_decl_info = let qt = match qt with | Some qt' -> qt' | None -> (* a qual_type was not found by the caller, so we try to get it out of property_impl_decl_info *) - (match property_impl_decl_info.Clang_ast_t.opidi_ivar_decl with + (match property_impl_decl_info.Clang_ast_t.opidi_ivar_decl with | Some decl_ref -> (match decl_ref.Clang_ast_t.dr_qual_type with | Some qt' -> qt' | None -> assert false) @@ -260,14 +255,14 @@ let make_decl_ref_exp_var (var_name, var_qt) var_kind stmt_info = let expr_info = make_expr_info var_qt in make_decl_ref_exp stmt_info expr_info (make_decl_ref_expr_info decl_ref) -let make_message_expr param_qt selector decl_ref_exp stmt_info = +let make_message_expr param_qt selector decl_ref_exp stmt_info add_cast = let stmt_info = stmt_info_with_fresh_pointer stmt_info in - let cast_expr = create_implicit_cast_expr stmt_info [decl_ref_exp] param_qt `LValueToRValue in - let parameters = [cast_expr] in - let obj_c_message_expr_info = { - omei_selector = selector; - omei_receiver_kind = `Instance - } in + let parameters = + if add_cast then + let cast_expr = create_implicit_cast_expr stmt_info [decl_ref_exp] param_qt `LValueToRValue in + [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 ObjCMessageExpr (stmt_info, parameters, expr_info, obj_c_message_expr_info) @@ -279,11 +274,22 @@ let make_binary_stmt stmt1 stmt2 stmt_info expr_info boi = let stmt_info = stmt_info_with_fresh_pointer stmt_info in BinaryOperator(stmt_info, [stmt1; stmt2], expr_info, boi) -let make_obj_c_message_expr_info_class selector qt = - { - omei_selector = selector; - omei_receiver_kind = `Class qt; - } +let make_next_object_exp stmt_info item items = + let var_decl_ref, var_type = + match item with + | DeclStmt (stmt_info, _, [VarDecl(di, var_name, var_type, _)]) -> + let decl_ref = make_general_decl_ref `Var 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)), + 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 let empty_var_decl = { vdi_storage_class = None; diff --git a/infer/src/clang/ast_expressions.mli b/infer/src/clang/ast_expressions.mli index 8c5052aaf..1cc86e530 100644 --- a/infer/src/clang/ast_expressions.mli +++ b/infer/src/clang/ast_expressions.mli @@ -37,9 +37,7 @@ val make_cast_expr : qual_type -> decl_info -> decl_ref_expr_info -> object_kind val make_self_field : string -> decl_info -> qual_type -> string -> stmt -val make_nondet_exp : stmt_info -> stmt - -val make_obj_c_message_expr_info : string -> string -> obj_c_message_expr_info +val make_next_object_exp : stmt_info -> stmt -> Clang_ast_t.stmt -> Clang_ast_t.stmt val create_nil : stmt_info -> stmt @@ -47,7 +45,7 @@ val create_implicit_cast_expr : stmt_info -> stmt list -> qual_type -> cast_kind val create_char_type : unit -> qual_type -val make_message_expr : qual_type -> string -> stmt -> stmt_info -> stmt +val make_message_expr : qual_type -> string -> stmt -> stmt_info -> bool -> stmt val make_compound_stmt : stmt list -> stmt_info -> stmt @@ -55,7 +53,9 @@ val make_decl_ref_exp_var : string * qual_type -> decl_kind -> stmt_info -> stmt val make_binary_stmt : stmt -> stmt -> stmt_info -> expr_info -> binary_operator_info -> stmt -val make_obj_c_message_expr_info_class : string -> qual_type -> obj_c_message_expr_info +val make_obj_c_message_expr_info_class : string -> string -> obj_c_message_expr_info + +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 diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index ddbf7bd4b..7e1476cc4 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -19,6 +19,8 @@ let nsstring_cl = "NSString" let nsobject_cl = "NSObject" +let next_object = "nextObject" + let nsautorelease_pool_cl = "NSAutoreleasePool" let id_cl = "id" diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 8e9a03948..ab250d933 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -47,6 +47,8 @@ val nsstring_cl : string val nsobject_cl : string +val next_object : string + val nsautorelease_pool_cl : string val string_with_utf8_m : string diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index e5941272f..f9c2c32d5 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -605,8 +605,7 @@ struct let fst_res_trans = instruction trans_state_param stmt in obj_c_message_expr_info, fst_res_trans with Self.SelfClassException class_name -> - let obj_c_message_expr_info = Ast_expressions.make_obj_c_message_expr_info_class selector - (Ast_expressions.create_qual_type class_name) in + let obj_c_message_expr_info = Ast_expressions.make_obj_c_message_expr_info_class selector class_name in obj_c_message_expr_info, empty_res_trans) in let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) rest in obj_c_message_expr_info, collect_res_trans (fst_res_trans :: l) @@ -1078,9 +1077,9 @@ struct let root_nodes, leaf_nodes = loop_instruction trans_state dowhile_kind stmt_info in { empty_res_trans with root_nodes = root_nodes; leaf_nodes = leaf_nodes } - and objCForCollectionStmt_trans trans_state cond body stmt_info = - let cond = Ast_expressions.make_nondet_exp stmt_info in - let while_kind = Loops.While (cond, body) in + and objCForCollectionStmt_trans trans_state item items body stmt_info = + let bin_op = Ast_expressions.make_next_object_exp stmt_info item items in + let while_kind = Loops.While (bin_op, body) in let root_nodes, leaf_nodes = loop_instruction trans_state while_kind stmt_info in { empty_res_trans with root_nodes = root_nodes; leaf_nodes = leaf_nodes } @@ -1498,14 +1497,14 @@ struct and objCBoxedExpr_trans trans_state info sel stmt_info stmts = let typ = CTypes_decl.class_from_pointer_type trans_state.context.tenv info.Clang_ast_t.ei_qual_type in - let obj_c_message_expr_info = Ast_expressions.make_obj_c_message_expr_info sel typ in + let obj_c_message_expr_info = Ast_expressions.make_obj_c_message_expr_info_class sel typ in let message_stmt = ObjCMessageExpr(stmt_info, stmts, info, obj_c_message_expr_info) in instruction trans_state message_stmt and objCArrayLiteral_trans trans_state info stmt_info stmts = let typ = CTypes_decl.class_from_pointer_type trans_state.context.tenv info.Clang_ast_t.ei_qual_type in let obj_c_message_expr_info = - Ast_expressions.make_obj_c_message_expr_info CFrontend_config.array_with_objects_count_m typ in + Ast_expressions.make_obj_c_message_expr_info_class CFrontend_config.array_with_objects_count_m typ in let stmts = stmts@[Ast_expressions.create_nil stmt_info] in let message_stmt = ObjCMessageExpr(stmt_info, stmts, info, obj_c_message_expr_info) in instruction trans_state message_stmt @@ -1513,7 +1512,7 @@ struct and objCDictionaryLiteral_trans trans_state info stmt_info stmts = let typ = CTypes_decl.class_from_pointer_type trans_state.context.tenv info.Clang_ast_t.ei_qual_type in let obj_c_message_expr_info = - Ast_expressions.make_obj_c_message_expr_info CFrontend_config.dict_with_objects_and_keys_m typ in + Ast_expressions.make_obj_c_message_expr_info_class CFrontend_config.dict_with_objects_and_keys_m typ in let stmts = swap_elements_list stmts in let stmts = stmts@[Ast_expressions.create_nil stmt_info] in let message_stmt = ObjCMessageExpr(stmt_info, stmts, info, obj_c_message_expr_info) in @@ -1524,7 +1523,7 @@ struct (Ast_expressions.create_char_type ()) `ArrayToPointerDecay] in let typ = CTypes_decl.class_from_pointer_type trans_state.context.tenv info.Clang_ast_t.ei_qual_type in let obj_c_message_expr_info = - Ast_expressions.make_obj_c_message_expr_info CFrontend_config.string_with_utf8_m typ in + Ast_expressions.make_obj_c_message_expr_info_class CFrontend_config.string_with_utf8_m typ in let message_stmt = ObjCMessageExpr(stmt_info, stmts, info, obj_c_message_expr_info) in instruction trans_state message_stmt @@ -1667,8 +1666,8 @@ struct | DoStmt(stmt_info, [body; cond]) -> doStmt_trans trans_state stmt_info cond body - | ObjCForCollectionStmt(stmt_info, [item; _; body]) -> - objCForCollectionStmt_trans trans_state item body stmt_info + | ObjCForCollectionStmt(stmt_info, [item; items; body]) -> + objCForCollectionStmt_trans trans_state item items body stmt_info | NullStmt(stmt_info, stmt_list) -> nullStmt_trans trans_state.succ_nodes stmt_info diff --git a/infer/src/clang/objcProperty_decl.ml b/infer/src/clang/objcProperty_decl.ml index 01cbb2dcf..f6f8eb8a6 100644 --- a/infer/src/clang/objcProperty_decl.ml +++ b/infer/src/clang/objcProperty_decl.ml @@ -315,15 +315,15 @@ let make_getter_setter cfg curr_class decl_info property_impl_decl_info = let param_decl = Ast_expressions.make_decl_ref_exp_var (param_name, qt_param) `ParmVar stmt_info in let retain_call = - Ast_expressions.make_message_expr qt_param retain param_decl stmt_info in + Ast_expressions.make_message_expr qt_param retain param_decl stmt_info true in let release_call = - Ast_expressions.make_message_expr qt_param release lhs_exp stmt_info in + Ast_expressions.make_message_expr qt_param release lhs_exp stmt_info true in [retain_call; release_call; setter] else if Ast_utils.is_copy memory_management_attribute then let param_decl = Ast_expressions.make_decl_ref_exp_var (param_name, qt_param) `ParmVar stmt_info in let copy_call = - Ast_expressions.make_message_expr qt_param copy param_decl stmt_info in + Ast_expressions.make_message_expr qt_param copy param_decl stmt_info true in let setter = Ast_expressions.make_binary_stmt lhs_exp copy_call stmt_info expr_info boi in [setter] diff --git a/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot b/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot index 3721a8b3e..809567dd2 100644 --- a/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot +++ b/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot @@ -1,25 +1,25 @@ digraph iCFG { -10 [label="10: DeclStmt \n n$6=_fun_NSString_stringWithUTF8String:(\"Mercedes-Benz\":char *) [line 14]\n n$7=_fun_NSString_stringWithUTF8String:(\"BMW\":char *) [line 14]\n n$8=_fun_NSString_stringWithUTF8String:(\"Porsche\":char *) [line 14]\n n$9=_fun_NSString_stringWithUTF8String:(\"Opel\":char *) [line 15]\n n$10=_fun_NSString_stringWithUTF8String:(\"Volkswagen\":char *) [line 15]\n n$11=_fun_NSString_stringWithUTF8String:(\"Audi\":char *) [line 15]\n n$5=_fun_NSArray_arrayWithObjects:count:(n$6:struct objc_object *,n$7:struct objc_object *,n$8:struct objc_object *,n$9:struct objc_object *,n$10:struct objc_object *,n$11:struct objc_object *,0:struct objc_object *) [line 14]\n *&germanCars:class NSArray *=n$5 [line 14]\n REMOVE_TEMPS(n$5,n$6,n$7,n$8,n$9,n$10,n$11); [line 14]\n " shape="box"] +10 [label="10: DeclStmt \n n$8=_fun_NSString_stringWithUTF8String:(\"Mercedes-Benz\":char *) [line 14]\n n$9=_fun_NSString_stringWithUTF8String:(\"BMW\":char *) [line 14]\n n$10=_fun_NSString_stringWithUTF8String:(\"Porsche\":char *) [line 14]\n n$11=_fun_NSString_stringWithUTF8String:(\"Opel\":char *) [line 15]\n n$12=_fun_NSString_stringWithUTF8String:(\"Volkswagen\":char *) [line 15]\n n$13=_fun_NSString_stringWithUTF8String:(\"Audi\":char *) [line 15]\n n$7=_fun_NSArray_arrayWithObjects:count:(n$8:struct objc_object *,n$9:struct objc_object *,n$10:struct objc_object *,n$11:struct objc_object *,n$12:struct objc_object *,n$13:struct objc_object *,0:struct objc_object *) [line 14]\n *&germanCars:class NSArray *=n$7 [line 14]\n REMOVE_TEMPS(n$7,n$8,n$9,n$10,n$11,n$12,n$13); [line 14]\n " shape="box"] 10 -> 9 ; -9 [label="9: BinaryOperatorStmt: Assign \n n$4=*&germanCars:class NSArray * [line 16]\n n$3=_fun_NSArray_objectAtIndexedSubscript:(n$4:class NSArray *,3:unsigned long ) virtual [line 16]\n *&s:class NSString *=n$3 [line 16]\n REMOVE_TEMPS(n$3,n$4); [line 16]\n NULLIFY(&germanCars,false); [line 16]\n NULLIFY(&s,false); [line 16]\n APPLY_ABSTRACTION; [line 16]\n " shape="box"] +9 [label="9: BinaryOperatorStmt: Assign \n n$6=*&germanCars:class NSArray * [line 16]\n n$5=_fun_NSArray_objectAtIndexedSubscript:(n$6:class NSArray *,3:unsigned long ) virtual [line 16]\n *&s:class NSString *=n$5 [line 16]\n REMOVE_TEMPS(n$5,n$6); [line 16]\n NULLIFY(&s,false); [line 16]\n APPLY_ABSTRACTION; [line 16]\n " shape="box"] 9 -> 4 ; -8 [label="8: Call _fun_NSLog \n n$1=_fun_NSString_stringWithUTF8String:(\"%@\":char *) [line 19]\n n$2=*&item:class NSString * [line 19]\n _fun_NSLog(n$1:struct objc_object *,n$2:class NSString *) [line 19]\n REMOVE_TEMPS(n$1,n$2); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] +8 [label="8: Call _fun_NSLog \n n$3=_fun_NSString_stringWithUTF8String:(\"%@\":char *) [line 19]\n n$4=*&item:class NSString * [line 19]\n _fun_NSLog(n$3:struct objc_object *,n$4:class NSString *) [line 19]\n REMOVE_TEMPS(n$3,n$4); [line 19]\n NULLIFY(&item,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] 8 -> 4 ; -7 [label="7: Prune (false branch) \n PRUNE(((n$0 > 0) == 0), false); [line 18]\n REMOVE_TEMPS(n$0); [line 18]\n " shape="invhouse"] +7 [label="7: Prune (false branch) \n PRUNE((n$2 == 0), false); [line 18]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 18]\n " shape="invhouse"] 7 -> 3 ; -6 [label="6: Prune (true branch) \n PRUNE(((n$0 > 0) != 0), true); [line 18]\n REMOVE_TEMPS(n$0); [line 18]\n " shape="invhouse"] +6 [label="6: Prune (true branch) \n PRUNE((n$2 != 0), true); [line 18]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 18]\n " shape="invhouse"] 6 -> 8 ; -5 [label="5: BinaryOperatorStmt: GT \n n$0=*&__INFER_NON_DET:int [line 18]\n " shape="box"] +5 [label="5: BinaryOperatorStmt: Assign \n n$1=*&germanCars:class NSArray * [line 18]\n n$0=_fun_NSArray_nextObject(n$1:class NSArray *) virtual [line 18]\n *&item:class NSString *=n$0 [line 18]\n n$2=*&item:class NSString * [line 18]\n " shape="box"] 5 -> 6 ; @@ -28,14 +28,14 @@ digraph iCFG { 4 -> 5 ; -3 [label="3: Return Stmt \n NULLIFY(&item,false); [line 22]\n *&return:int =0 [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] +3 [label="3: Return Stmt \n NULLIFY(&germanCars,false); [line 22]\n NULLIFY(&item,false); [line 22]\n *&return:int =0 [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] 3 -> 2 ; 2 [label="2: Exit main \n " color=yellow style=filled] -1 [label="1: Start main\nFormals: \nLocals: s:class NSString * germanCars:class NSArray * item:class NSString * \n DECLARE_LOCALS(&return,&s,&germanCars,&item); [line 10]\n NULLIFY(&germanCars,false); [line 10]\n NULLIFY(&s,false); [line 10]\n " color=yellow style=filled] +1 [label="1: Start main\nFormals: \nLocals: s:class NSString * germanCars:class NSArray * item:class NSString * \n DECLARE_LOCALS(&return,&s,&germanCars,&item); [line 10]\n NULLIFY(&germanCars,false); [line 10]\n NULLIFY(&item,false); [line 10]\n NULLIFY(&s,false); [line 10]\n " color=yellow style=filled] 1 -> 10 ; diff --git a/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot new file mode 100644 index 000000000..4b504e4c6 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot @@ -0,0 +1,78 @@ +digraph iCFG { +19 [label="19: DeclStmt \n *&size:int =0 [line 17]\n " shape="box"] + + + 19 -> 18 ; +18 [label="18: DeclStmt \n *&item:class NSArray *=0 [line 18]\n NULLIFY(&item,false); [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + + + 18 -> 13 ; +17 [label="17: ComppoundAssignStmt \n n$12=*&item:class NSArray * [line 20]\n n$11=_fun_NSArray_count(n$12:class NSArray *) virtual [line 20]\n n$13=*&size:int [line 20]\n *&size:int =(n$13 + n$11) [line 20]\n REMOVE_TEMPS(n$11,n$12,n$13); [line 20]\n NULLIFY(&item,false); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] + + + 17 -> 13 ; +16 [label="16: Prune (false branch) \n PRUNE((n$10 == 0), false); [line 19]\n REMOVE_TEMPS(n$8,n$9,n$10); [line 19]\n " shape="invhouse"] + + + 16 -> 12 ; +15 [label="15: Prune (true branch) \n PRUNE((n$10 != 0), true); [line 19]\n REMOVE_TEMPS(n$8,n$9,n$10); [line 19]\n " shape="invhouse"] + + + 15 -> 17 ; +14 [label="14: BinaryOperatorStmt: Assign \n n$9=*&items:class NSArray * [line 19]\n n$8=_fun_NSArray_nextObject(n$9:class NSArray *) virtual [line 19]\n *&item:class NSArray *=n$8 [line 19]\n n$10=*&item:class NSArray * [line 19]\n " shape="box"] + + + 14 -> 15 ; + 14 -> 16 ; +13 [label="13: + \n " ] + + + 13 -> 14 ; +12 [label="12: Return Stmt \n NULLIFY(&item,false); [line 22]\n NULLIFY(&items,false); [line 22]\n n$7=*&size:int [line 22]\n *&return:int =n$7 [line 22]\n REMOVE_TEMPS(n$7); [line 22]\n NULLIFY(&size,false); [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Exit A_while_loop: \n " color=yellow style=filled] + + +10 [label="10: Start A_while_loop:\nFormals: self:class A * items:class NSArray *\nLocals: size:int item:class NSArray * \n DECLARE_LOCALS(&return,&size,&item); [line 16]\n NULLIFY(&item,false); [line 16]\n NULLIFY(&self,false); [line 16]\n NULLIFY(&size,false); [line 16]\n " color=yellow style=filled] + + + 10 -> 19 ; +9 [label="9: DeclStmt \n *&size:int =0 [line 9]\n APPLY_ABSTRACTION; [line 9]\n " shape="box"] + + + 9 -> 4 ; +8 [label="8: ComppoundAssignStmt \n n$5=*&item:class NSArray * [line 11]\n n$4=_fun_NSArray_count(n$5:class NSArray *) virtual [line 11]\n n$6=*&size:int [line 11]\n *&size:int =(n$6 + n$4) [line 11]\n REMOVE_TEMPS(n$4,n$5,n$6); [line 11]\n NULLIFY(&item,false); [line 11]\n APPLY_ABSTRACTION; [line 11]\n " shape="box"] + + + 8 -> 4 ; +7 [label="7: Prune (false branch) \n PRUNE((n$3 == 0), false); [line 10]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 10]\n " shape="invhouse"] + + + 7 -> 3 ; +6 [label="6: Prune (true branch) \n PRUNE((n$3 != 0), true); [line 10]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 10]\n " shape="invhouse"] + + + 6 -> 8 ; +5 [label="5: BinaryOperatorStmt: Assign \n n$2=*&items:class NSArray * [line 10]\n n$1=_fun_NSArray_nextObject(n$2:class NSArray *) virtual [line 10]\n *&item:class NSArray *=n$1 [line 10]\n n$3=*&item:class NSArray * [line 10]\n " shape="box"] + + + 5 -> 6 ; + 5 -> 7 ; +4 [label="4: + \n " ] + + + 4 -> 5 ; +3 [label="3: Return Stmt \n NULLIFY(&item,false); [line 13]\n NULLIFY(&items,false); [line 13]\n n$0=*&size:int [line 13]\n *&return:int =n$0 [line 13]\n REMOVE_TEMPS(n$0); [line 13]\n NULLIFY(&size,false); [line 13]\n APPLY_ABSTRACTION; [line 13]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit A_fast_loop: \n " color=yellow style=filled] + + +1 [label="1: Start A_fast_loop:\nFormals: self:class A * items:class NSArray *\nLocals: size:int item:class NSArray * \n DECLARE_LOCALS(&return,&size,&item); [line 8]\n NULLIFY(&item,false); [line 8]\n NULLIFY(&self,false); [line 8]\n NULLIFY(&size,false); [line 8]\n " color=yellow style=filled] + + + 1 -> 9 ; +} diff --git a/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.m b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.m new file mode 100644 index 000000000..ad5ed4e66 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.m @@ -0,0 +1,25 @@ +#import + +@interface A : NSObject +@end + +@implementation A + +- (int) fast_loop: (NSArray *) items { + int size = 0; + for (NSArray* item in items) { + size += [item count]; + } + return size; +} + +- (int) while_loop: (NSArray*) items { + int size = 0; + NSArray* item = nil; + while (item = [items nextObject]) { + size += [item count]; + } + return size; +} + +@end diff --git a/infer/tests/frontend/objc/FastEnumerationTest.java b/infer/tests/frontend/objc/FastEnumerationTest.java new file mode 100644 index 000000000..28e8877d1 --- /dev/null +++ b/infer/tests/frontend/objc/FastEnumerationTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013- Facebook. + * All rights reserved. + */ + +package frontend.objc; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.DotFilesEqual.dotFileEqualTo; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferRunner; + + +public class FastEnumerationTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunOnTestThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + + String exception_src = + "infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.m"; + + String exception_dotty = + "infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot"; + + ImmutableList inferCmd = + InferRunner.createObjCInferCommandFrontend(folder, exception_src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + exception_src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(exception_dotty)); + } +}