diff --git a/infer/src/clang/cFrontend_checkers.ml b/infer/src/clang/cFrontend_checkers.ml index 8b30b558b..590e31ba7 100644 --- a/infer/src/clang/cFrontend_checkers.ml +++ b/infer/src/clang/cFrontend_checkers.ml @@ -70,18 +70,11 @@ let call_method_on_nth pred pn m st = with _ -> false) | _ -> false -(* st |= EF (atomic_pred param) *) -let rec exists_eventually_st atomic_pred param st = - if atomic_pred param st then true - else - let _, st_list = Clang_ast_proj.get_stmt_tuple st in - IList.exists (exists_eventually_st atomic_pred param) st_list - let dec_body_eventually atomic_pred param dec = match dec with | Clang_ast_t.ObjCMethodDecl (_, _, omdi) -> (match omdi.Clang_ast_t.omdi_body with - | Some body -> exists_eventually_st atomic_pred param body + | Some body -> Ast_utils.exists_eventually_st atomic_pred param body | _ -> false) | _ -> false diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 37b66449c..439db43f5 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -392,6 +392,13 @@ struct let type_ptr = match decl_ref.Clang_ast_t.dr_type_ptr with Some tp -> tp | _ -> assert false in name_info, decl_ptr, type_ptr + (* st |= EF (atomic_pred param) *) + let rec exists_eventually_st atomic_pred param st = + if atomic_pred param st then true + else + let _, st_list = Clang_ast_proj.get_stmt_tuple st in + IList.exists (exists_eventually_st atomic_pred param) st_list + (* let rec getter_attribute_opt attributes = match attributes with diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index b250dae30..09408c8df 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -133,6 +133,8 @@ sig val get_info_from_decl_ref : Clang_ast_t.decl_ref -> Clang_ast_t.named_decl_info * Clang_ast_t.pointer * Clang_ast_t.type_ptr + val exists_eventually_st : ('a -> Clang_ast_t.stmt -> bool) -> 'a -> Clang_ast_t.stmt -> bool + end module General_utils : diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index d898f3c91..37565e5e3 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -1838,7 +1838,7 @@ struct | _ -> assert false (* Cast expression are treated the same apart from the cast operation kind*) - and cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_expr_info is_objc_bridged = + and cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_expr_info = let context = trans_state.context in Printing.log_out " priority node free = '%s'\n@." (string_of_bool (PriorityNode.is_priority_free trans_state)); @@ -1850,8 +1850,11 @@ struct CTypes_decl.type_ptr_to_sil_type context.CContext.tenv expr_info.Clang_ast_t.ei_type_ptr in let cast_kind = cast_expr_info.Clang_ast_t.cei_cast_kind in (* This gives the differnece among cast operations kind*) + let is_objc_bridged_cast_expr _ stmt = + match stmt with | Clang_ast_t.ObjCBridgedCastExpr _ -> true | _ -> false in + let is_objc_bridged = Ast_utils.exists_eventually_st is_objc_bridged_cast_expr () stmt in let cast_ids, cast_inst, cast_exp = - cast_operation context cast_kind res_trans_stmt.exps typ sil_loc is_objc_bridged in + cast_operation trans_state cast_kind res_trans_stmt.exps typ sil_loc is_objc_bridged in { res_trans_stmt with ids = res_trans_stmt.ids @ cast_ids; instrs = res_trans_stmt.instrs @ cast_inst; @@ -2329,6 +2332,13 @@ struct "CXXStdInitializerListExpr" stmt_info all_res_trans in { res_trans_to_parent with exps = res_trans_call.exps } + and objCBridgedCastExpr_trans trans_state stmts expr_info = + let stmt = extract_stmt_from_singleton stmts "" in + let tenv = trans_state.context.CContext.tenv in + let typ = CTypes_decl.get_type_from_expr_info expr_info tenv in + let trans_state' = { trans_state with obj_bridged_cast_typ = Some typ } in + instruction trans_state' stmt + (* Translates a clang instruction into SIL instructions. It takes a *) (* a trans_state containing current info on the translation and it returns *) (* a result_state.*) @@ -2439,15 +2449,16 @@ struct | UnaryExprOrTypeTraitExpr(_, _, expr_info, ei) -> unaryExprOrTypeTraitExpr_trans trans_state expr_info ei - | ObjCBridgedCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _) -> - cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind true + | ObjCBridgedCastExpr(_, stmt_list, expr_info, _, _) -> + objCBridgedCastExpr_trans trans_state stmt_list expr_info + | ImplicitCastExpr(stmt_info, stmt_list, expr_info, cast_kind) | CStyleCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _) | CXXReinterpretCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _) | CXXConstCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _) | CXXStaticCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _) | CXXFunctionalCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _)-> - cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind false + cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind | IntegerLiteral(_, _, expr_info, integer_literal_info) -> integerLiteral_trans trans_state expr_info integer_literal_info @@ -2686,6 +2697,7 @@ struct priority = Free; var_exp_typ = None; opaque_exp = None; + obj_bridged_cast_typ = None; } in let res_trans_stmt = instruction trans_state stmt in fst (CTrans_utils.extract_exp_from_list res_trans_stmt.exps warning) @@ -2698,6 +2710,7 @@ struct priority = Free; var_exp_typ = None; opaque_exp = None; + obj_bridged_cast_typ = None } in let instrs = extra_instrs @ [`ClangStmt body] in let instrs_trans = IList.map get_custom_stmt_trans instrs in diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index c61660268..15ba865c8 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -132,6 +132,7 @@ type trans_state = { priority: priority_node; var_exp_typ: (Sil.exp * Sil.typ) option; opaque_exp: (Sil.exp * Sil.typ) option; + obj_bridged_cast_typ : Sil.typ option } (* A translation result. It is returned by the translation function. *) @@ -414,35 +415,38 @@ let dereference_value_from_result sil_loc trans_result ~strip_pointer = } -let cast_operation context cast_kind exps cast_typ sil_loc is_objc_bridged = +let cast_operation trans_state cast_kind exps cast_typ sil_loc is_objc_bridged = let (exp, typ) = extract_exp_from_list exps "" in - let exp_typ = match cast_kind with - | `UncheckedDerivedToBase | `DerivedToBase -> typ (* These casts ignore change of type *) - | _ -> cast_typ (* by default use the return type of cast expr *) in - if is_objc_bridged then - let id, instr, exp = create_cast_instrs context exp typ cast_typ sil_loc in - [id], [instr], (exp, exp_typ) - else - match cast_kind with - | `NoOp - | `BitCast - | `IntegralCast - | `DerivedToBase - | `UncheckedDerivedToBase - | `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*) - ([], [], (exp, exp_typ)) - | `LValueToRValue -> - (* Takes an LValue and allow it to use it as RValue. *) - (* So we assign the LValue to a temp and we pass it to the parent.*) - let ids, instrs, deref_exp = dereference_var_sil (exp, typ) sil_loc in - ids, instrs, (deref_exp, exp_typ) - | `CPointerToObjCPointerCast -> - [], [], (Sil.Cast(typ, exp), exp_typ) - | _ -> - Printing.log_err - "\nWARNING: Missing translation for Cast Kind %s. The construct has been ignored...\n" - (Clang_ast_j.string_of_cast_kind cast_kind); - ([],[], (exp, exp_typ)) + let is_objc_bridged = Option.is_some trans_state.obj_bridged_cast_typ || is_objc_bridged in + match cast_kind with + | `DerivedToBase + | `UncheckedDerivedToBase -> (* These casts ignore change of type *) + ([], [], (exp, typ)) + | `NoOp + | `BitCast + | `IntegralCast + | `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*) + ([], [], (exp, cast_typ)) + | `CPointerToObjCPointerCast + | `ARCProduceObject + | `ARCConsumeObject when is_objc_bridged -> + (* Translation of __bridge_transfer or __bridge_retained *) + let objc_cast_typ = + match trans_state.obj_bridged_cast_typ with + | Some typ -> typ + | None -> cast_typ in + let id, instr, exp = create_cast_instrs trans_state.context exp typ objc_cast_typ sil_loc in + [id], [instr], (exp, cast_typ) + | `LValueToRValue -> + (* Takes an LValue and allow it to use it as RValue. *) + (* So we assign the LValue to a temp and we pass it to the parent.*) + let ids, instrs, deref_exp = dereference_var_sil (exp, typ) sil_loc in + ids, instrs, (deref_exp, cast_typ) + | _ -> + Printing.log_err + "\nWARNING: Missing translation for Cast Kind %s. The construct has been ignored...\n" + (Clang_ast_j.string_of_cast_kind cast_kind); + ([],[], (exp, cast_typ)) let trans_assertion_failure sil_loc context = let assert_fail_builtin = Sil.Const (Sil.Cfun ModelBuiltins.__infer_fail) in diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 62a90d79d..8cef73974 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -28,6 +28,7 @@ type trans_state = { priority: priority_node; var_exp_typ: (Sil.exp * Sil.typ) option; opaque_exp: (Sil.exp * Sil.typ) option; + obj_bridged_cast_typ : Sil.typ option } type trans_result = { @@ -81,7 +82,7 @@ val get_type_from_exp_stmt : Clang_ast_t.stmt -> Clang_ast_t.type_ptr val dereference_value_from_result : Location.t -> trans_result -> strip_pointer:bool -> trans_result val cast_operation : - CContext.t -> Clang_ast_t.cast_kind -> (Sil.exp * Sil.typ) list -> Sil.typ -> Location.t -> + trans_state -> Clang_ast_t.cast_kind -> (Sil.exp * Sil.typ) list -> Sil.typ -> Location.t -> bool -> Ident.t list * Sil.instr list * (Sil.exp * Sil.typ) val trans_assertion_failure : Location.t -> CContext.t -> trans_result diff --git a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.dot b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.dot index e0769049a..186fd75dc 100644 --- a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.dot +++ b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.dot @@ -1,4 +1,19 @@ digraph iCFG { +23 [label="23: DeclStmt \n n$1=_fun_NSDictionary_dictionaryWithObjects:forKeys:count:(0:struct objc_object *) [line 45]\n n$2=_fun_NSString_stringWithUTF8String:(\"key\":char *) [line 45]\n n$3=_fun_NSDictionary_dictionaryWithObjects:forKeys:count:(n$1:struct objc_object *,n$2:struct objc_object *,0:struct objc_object *) [line 45]\n *&bufferAttributes:class NSDictionary *=n$3 [line 45]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 45]\n " shape="box"] + + + 23 -> 22 ; +22 [label="22: DeclStmt \n n$0=*&bufferAttributes:class NSDictionary * [line 46]\n *&dict:struct __CFDictionary *=n$0 [line 46]\n REMOVE_TEMPS(n$0); [line 46]\n NULLIFY(&bufferAttributes,false); [line 46]\n NULLIFY(&dict,false); [line 46]\n APPLY_ABSTRACTION; [line 46]\n " shape="box"] + + + 22 -> 21 ; +21 [label="21: Exit bridgeDictionaryNoLeak \n " color=yellow style=filled] + + +20 [label="20: Start bridgeDictionaryNoLeak\nFormals: \nLocals: dict:struct __CFDictionary * bufferAttributes:class NSDictionary * \n DECLARE_LOCALS(&return,&dict,&bufferAttributes); [line 44]\n NULLIFY(&bufferAttributes,false); [line 44]\n NULLIFY(&dict,false); [line 44]\n " color=yellow style=filled] + + + 20 -> 23 ; 19 [label="19: Return Stmt \n n$0=_fun___builtin___CFStringMakeConstantString(\"Icon\":char *) [line 41]\n n$1=_fun_CTFontCreateWithName(n$0:struct __CFString *,17.000000:double ,0:struct CGAffineTransform *) [line 41]\n n$2=_fun___objc_cast(n$1:void *,sizeof(void ):unsigned long ) [line 41]\n *&return:struct __CTFont *=n$2 [line 41]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 41]\n APPLY_ABSTRACTION; [line 41]\n " shape="box"] @@ -44,7 +59,7 @@ digraph iCFG { 8 -> 7 ; -7 [label="7: DeclStmt \n n$2=*&nameRef:struct __CFLocale * [line 26]\n *&a:class NSLocale *=(struct __CFLocale *)n$2 [line 26]\n REMOVE_TEMPS(n$2); [line 26]\n NULLIFY(&a,false); [line 26]\n NULLIFY(&nameRef,false); [line 26]\n APPLY_ABSTRACTION; [line 26]\n " shape="box"] +7 [label="7: DeclStmt \n n$2=*&nameRef:struct __CFLocale * [line 26]\n *&a:class NSLocale *=n$2 [line 26]\n REMOVE_TEMPS(n$2); [line 26]\n NULLIFY(&a,false); [line 26]\n NULLIFY(&nameRef,false); [line 26]\n APPLY_ABSTRACTION; [line 26]\n " shape="box"] 7 -> 6 ; @@ -59,7 +74,7 @@ digraph iCFG { 4 -> 3 ; -3 [label="3: DeclStmt \n n$0=*&nameRef:struct __CFLocale * [line 21]\n *&a:class NSLocale *=(struct __CFLocale *)n$0 [line 21]\n REMOVE_TEMPS(n$0); [line 21]\n NULLIFY(&a,false); [line 21]\n NULLIFY(&nameRef,false); [line 21]\n APPLY_ABSTRACTION; [line 21]\n " shape="box"] +3 [label="3: DeclStmt \n n$0=*&nameRef:struct __CFLocale * [line 21]\n *&a:class NSLocale *=n$0 [line 21]\n REMOVE_TEMPS(n$0); [line 21]\n NULLIFY(&a,false); [line 21]\n NULLIFY(&nameRef,false); [line 21]\n APPLY_ABSTRACTION; [line 21]\n " shape="box"] 3 -> 2 ; diff --git a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.m b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.m index 9ce9d887f..1835e0aa8 100644 --- a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.m +++ b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/TollBridgeExample.m @@ -41,4 +41,9 @@ CTFontRef cfautorelease_test() { return CFAutorelease(CTFontCreateWithName(CFSTR("Icon"), 17.0, NULL)); } +void bridgeDictionaryNoLeak() { + NSDictionary* bufferAttributes = @{(NSString*)@"key" : @{} }; + CFDictionaryRef dict = (__bridge CFDictionaryRef)bufferAttributes; +} + @end diff --git a/infer/tests/codetoanalyze/objc/frontend/assertions/NSAssert_example.dot b/infer/tests/codetoanalyze/objc/frontend/assertions/NSAssert_example.dot index 2437dfb86..e74a5101c 100644 --- a/infer/tests/codetoanalyze/objc/frontend/assertions/NSAssert_example.dot +++ b/infer/tests/codetoanalyze/objc/frontend/assertions/NSAssert_example.dot @@ -197,11 +197,11 @@ digraph iCFG { 58 -> 54 ; -57 [label="57: Prune (false branch) \n PRUNE(((n$2 != (void *)0) == 0), false); [line 31]\n REMOVE_TEMPS(n$2); [line 31]\n " shape="invhouse"] +57 [label="57: Prune (false branch) \n PRUNE(((n$2 != 0) == 0), false); [line 31]\n REMOVE_TEMPS(n$2); [line 31]\n " shape="invhouse"] 57 -> 59 ; -56 [label="56: Prune (true branch) \n PRUNE(((n$2 != (void *)0) != 0), true); [line 31]\n REMOVE_TEMPS(n$2); [line 31]\n " shape="invhouse"] +56 [label="56: Prune (true branch) \n PRUNE(((n$2 != 0) != 0), true); [line 31]\n REMOVE_TEMPS(n$2); [line 31]\n " shape="invhouse"] 56 -> 58 ; @@ -292,11 +292,11 @@ digraph iCFG { 35 -> 31 ; -34 [label="34: Prune (false branch) \n PRUNE(((n$17 != (void *)0) == 0), false); [line 24]\n REMOVE_TEMPS(n$17); [line 24]\n " shape="invhouse"] +34 [label="34: Prune (false branch) \n PRUNE(((n$17 != 0) == 0), false); [line 24]\n REMOVE_TEMPS(n$17); [line 24]\n " shape="invhouse"] 34 -> 36 ; -33 [label="33: Prune (true branch) \n PRUNE(((n$17 != (void *)0) != 0), true); [line 24]\n REMOVE_TEMPS(n$17); [line 24]\n " shape="invhouse"] +33 [label="33: Prune (true branch) \n PRUNE(((n$17 != 0) != 0), true); [line 24]\n REMOVE_TEMPS(n$17); [line 24]\n " shape="invhouse"] 33 -> 35 ; @@ -387,11 +387,11 @@ digraph iCFG { 12 -> 8 ; -11 [label="11: Prune (false branch) \n PRUNE(((n$2 != (void *)0) == 0), false); [line 19]\n REMOVE_TEMPS(n$2); [line 19]\n " shape="invhouse"] +11 [label="11: Prune (false branch) \n PRUNE(((n$2 != 0) == 0), false); [line 19]\n REMOVE_TEMPS(n$2); [line 19]\n " shape="invhouse"] 11 -> 13 ; -10 [label="10: Prune (true branch) \n PRUNE(((n$2 != (void *)0) != 0), true); [line 19]\n REMOVE_TEMPS(n$2); [line 19]\n " shape="invhouse"] +10 [label="10: Prune (true branch) \n PRUNE(((n$2 != 0) != 0), true); [line 19]\n REMOVE_TEMPS(n$2); [line 19]\n " shape="invhouse"] 10 -> 12 ; diff --git a/infer/tests/endtoend/objc/TollBridgeTest.java b/infer/tests/endtoend/objc/TollBridgeTest.java index eded568c5..d657c1bf8 100644 --- a/infer/tests/endtoend/objc/TollBridgeTest.java +++ b/infer/tests/endtoend/objc/TollBridgeTest.java @@ -12,6 +12,7 @@ package endtoend.objc; import static org.hamcrest.MatcherAssert.assertThat; import static utils.InferError.inferError; import static utils.matchers.ResultContainsErrorInMethod.contains; +import static utils.matchers.ResultContainsExactly.containsExactly; import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain; import com.google.common.collect.ImmutableList; @@ -50,74 +51,20 @@ public class TollBridgeTest { } @Test - public void whenInferRunsOnTollBridgeExampleThenMLIsNotFound() + public void whenInferRunsOnTollBridgeExampleThenMLIsFound() throws InterruptedException, IOException, InferException { InferResults inferResults = InferRunner.runInferObjC(inferCmd); assertThat( - "Results should not contain memory leak", + "Results should contain memory leaks", inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "bridgeTransfer")); - } - - @Test - public void whenInferRunsOnTollBridgeExampleTest1ThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "bridge")); - } - - @Test - public void whenInferRunsOnTollBridgeExampleTest2ThenMLIsFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should contain memory leak", - inferResults, - contains( + containsExactly( MEMORY_LEAK, memory_leak_file, - "brideRetained" + new String[]{ + "bridge", + "brideRetained", + } ) ); } - - @Test - public void whenInferRunsOnTollBridgeExampleTest3ThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "_readHTTPHeader" - ) - ); - } - - @Test - public void whenInferRunsOnCfautorelease_testThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "cfautorelease_test" - ) - ); - } - }