Fix toll free bridging

Reviewed By: ddino

Differential Revision: D3185074

fb-gh-sync-id: 42993fb
fbshipit-source-id: 42993fb
master
Dulma Rodriguez 9 years ago committed by Facebook Github Bot 8
parent 1be7827981
commit be91fb5429

@ -70,18 +70,11 @@ let call_method_on_nth pred pn m st =
with _ -> false) with _ -> false)
| _ -> 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 = let dec_body_eventually atomic_pred param dec =
match dec with match dec with
| Clang_ast_t.ObjCMethodDecl (_, _, omdi) -> | Clang_ast_t.ObjCMethodDecl (_, _, omdi) ->
(match omdi.Clang_ast_t.omdi_body with (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)
| _ -> false | _ -> false

@ -392,6 +392,13 @@ struct
let type_ptr = match decl_ref.Clang_ast_t.dr_type_ptr with Some tp -> tp | _ -> assert false in 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 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 = let rec getter_attribute_opt attributes =
match attributes with match attributes with

@ -133,6 +133,8 @@ sig
val get_info_from_decl_ref : Clang_ast_t.decl_ref -> 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 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 end
module General_utils : module General_utils :

@ -1838,7 +1838,7 @@ struct
| _ -> assert false | _ -> assert false
(* Cast expression are treated the same apart from the cast operation kind*) (* 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 let context = trans_state.context in
Printing.log_out " priority node free = '%s'\n@." Printing.log_out " priority node free = '%s'\n@."
(string_of_bool (PriorityNode.is_priority_free trans_state)); (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 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 let cast_kind = cast_expr_info.Clang_ast_t.cei_cast_kind in
(* This gives the differnece among cast operations kind*) (* 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 = 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 { res_trans_stmt with
ids = res_trans_stmt.ids @ cast_ids; ids = res_trans_stmt.ids @ cast_ids;
instrs = res_trans_stmt.instrs @ cast_inst; instrs = res_trans_stmt.instrs @ cast_inst;
@ -2329,6 +2332,13 @@ struct
"CXXStdInitializerListExpr" stmt_info all_res_trans in "CXXStdInitializerListExpr" stmt_info all_res_trans in
{ res_trans_to_parent with exps = res_trans_call.exps } { 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 *) (* Translates a clang instruction into SIL instructions. It takes a *)
(* a trans_state containing current info on the translation and it returns *) (* a trans_state containing current info on the translation and it returns *)
(* a result_state.*) (* a result_state.*)
@ -2439,15 +2449,16 @@ struct
| UnaryExprOrTypeTraitExpr(_, _, expr_info, ei) -> | UnaryExprOrTypeTraitExpr(_, _, expr_info, ei) ->
unaryExprOrTypeTraitExpr_trans trans_state expr_info ei unaryExprOrTypeTraitExpr_trans trans_state expr_info ei
| ObjCBridgedCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _) -> | ObjCBridgedCastExpr(_, stmt_list, expr_info, _, _) ->
cast_exprs_trans trans_state stmt_info stmt_list expr_info cast_kind true objCBridgedCastExpr_trans trans_state stmt_list expr_info
| ImplicitCastExpr(stmt_info, stmt_list, expr_info, cast_kind) | ImplicitCastExpr(stmt_info, stmt_list, expr_info, cast_kind)
| CStyleCastExpr(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, _, _) | CXXReinterpretCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _)
| CXXConstCastExpr(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, _, _) | CXXStaticCastExpr(stmt_info, stmt_list, expr_info, cast_kind, _, _)
| CXXFunctionalCastExpr(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(_, _, expr_info, integer_literal_info) ->
integerLiteral_trans trans_state expr_info integer_literal_info integerLiteral_trans trans_state expr_info integer_literal_info
@ -2686,6 +2697,7 @@ struct
priority = Free; priority = Free;
var_exp_typ = None; var_exp_typ = None;
opaque_exp = None; opaque_exp = None;
obj_bridged_cast_typ = None;
} in } in
let res_trans_stmt = instruction trans_state stmt in let res_trans_stmt = instruction trans_state stmt in
fst (CTrans_utils.extract_exp_from_list res_trans_stmt.exps warning) fst (CTrans_utils.extract_exp_from_list res_trans_stmt.exps warning)
@ -2698,6 +2710,7 @@ struct
priority = Free; priority = Free;
var_exp_typ = None; var_exp_typ = None;
opaque_exp = None; opaque_exp = None;
obj_bridged_cast_typ = None
} in } in
let instrs = extra_instrs @ [`ClangStmt body] in let instrs = extra_instrs @ [`ClangStmt body] in
let instrs_trans = IList.map get_custom_stmt_trans instrs in let instrs_trans = IList.map get_custom_stmt_trans instrs in

@ -132,6 +132,7 @@ type trans_state = {
priority: priority_node; priority: priority_node;
var_exp_typ: (Sil.exp * Sil.typ) option; var_exp_typ: (Sil.exp * Sil.typ) option;
opaque_exp: (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. *) (* 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) = extract_exp_from_list exps "" in
let exp_typ = match cast_kind with let is_objc_bridged = Option.is_some trans_state.obj_bridged_cast_typ || is_objc_bridged in
| `UncheckedDerivedToBase | `DerivedToBase -> typ (* These casts ignore change of type *) match cast_kind with
| _ -> cast_typ (* by default use the return type of cast expr *) in | `DerivedToBase
if is_objc_bridged then | `UncheckedDerivedToBase -> (* These casts ignore change of type *)
let id, instr, exp = create_cast_instrs context exp typ cast_typ sil_loc in ([], [], (exp, typ))
[id], [instr], (exp, exp_typ) | `NoOp
else | `BitCast
match cast_kind with | `IntegralCast
| `NoOp | `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*)
| `BitCast ([], [], (exp, cast_typ))
| `IntegralCast | `CPointerToObjCPointerCast
| `DerivedToBase | `ARCProduceObject
| `UncheckedDerivedToBase | `ARCConsumeObject when is_objc_bridged ->
| `IntegralToBoolean -> (* This is treated as a nop by returning the same expressions exps*) (* Translation of __bridge_transfer or __bridge_retained *)
([], [], (exp, exp_typ)) let objc_cast_typ =
| `LValueToRValue -> match trans_state.obj_bridged_cast_typ with
(* Takes an LValue and allow it to use it as RValue. *) | Some typ -> typ
(* So we assign the LValue to a temp and we pass it to the parent.*) | None -> cast_typ in
let ids, instrs, deref_exp = dereference_var_sil (exp, typ) sil_loc in let id, instr, exp = create_cast_instrs trans_state.context exp typ objc_cast_typ sil_loc in
ids, instrs, (deref_exp, exp_typ) [id], [instr], (exp, cast_typ)
| `CPointerToObjCPointerCast -> | `LValueToRValue ->
[], [], (Sil.Cast(typ, exp), exp_typ) (* 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.*)
Printing.log_err let ids, instrs, deref_exp = dereference_var_sil (exp, typ) sil_loc in
"\nWARNING: Missing translation for Cast Kind %s. The construct has been ignored...\n" ids, instrs, (deref_exp, cast_typ)
(Clang_ast_j.string_of_cast_kind cast_kind); | _ ->
([],[], (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, cast_typ))
let trans_assertion_failure sil_loc context = let trans_assertion_failure sil_loc context =
let assert_fail_builtin = Sil.Const (Sil.Cfun ModelBuiltins.__infer_fail) in let assert_fail_builtin = Sil.Const (Sil.Cfun ModelBuiltins.__infer_fail) in

@ -28,6 +28,7 @@ type trans_state = {
priority: priority_node; priority: priority_node;
var_exp_typ: (Sil.exp * Sil.typ) option; var_exp_typ: (Sil.exp * Sil.typ) option;
opaque_exp: (Sil.exp * Sil.typ) option; opaque_exp: (Sil.exp * Sil.typ) option;
obj_bridged_cast_typ : Sil.typ option
} }
type trans_result = { 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 dereference_value_from_result : Location.t -> trans_result -> strip_pointer:bool -> trans_result
val cast_operation : 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) bool -> Ident.t list * Sil.instr list * (Sil.exp * Sil.typ)
val trans_assertion_failure : Location.t -> CContext.t -> trans_result val trans_assertion_failure : Location.t -> CContext.t -> trans_result

@ -1,4 +1,19 @@
digraph iCFG { 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"] 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 ; 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 ; 7 -> 6 ;
@ -59,7 +74,7 @@ digraph iCFG {
4 -> 3 ; 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 ; 3 -> 2 ;

@ -41,4 +41,9 @@ CTFontRef cfautorelease_test() {
return CFAutorelease(CTFontCreateWithName(CFSTR("Icon"), 17.0, NULL)); return CFAutorelease(CTFontCreateWithName(CFSTR("Icon"), 17.0, NULL));
} }
void bridgeDictionaryNoLeak() {
NSDictionary* bufferAttributes = @{(NSString*)@"key" : @{} };
CFDictionaryRef dict = (__bridge CFDictionaryRef)bufferAttributes;
}
@end @end

@ -197,11 +197,11 @@ digraph iCFG {
58 -> 54 ; 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 ; 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 ; 56 -> 58 ;
@ -292,11 +292,11 @@ digraph iCFG {
35 -> 31 ; 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 ; 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 ; 33 -> 35 ;
@ -387,11 +387,11 @@ digraph iCFG {
12 -> 8 ; 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 ; 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 ; 10 -> 12 ;

@ -12,6 +12,7 @@ package endtoend.objc;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static utils.InferError.inferError; import static utils.InferError.inferError;
import static utils.matchers.ResultContainsErrorInMethod.contains; import static utils.matchers.ResultContainsErrorInMethod.contains;
import static utils.matchers.ResultContainsExactly.containsExactly;
import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain; import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -50,74 +51,20 @@ public class TollBridgeTest {
} }
@Test @Test
public void whenInferRunsOnTollBridgeExampleThenMLIsNotFound() public void whenInferRunsOnTollBridgeExampleThenMLIsFound()
throws InterruptedException, IOException, InferException { throws InterruptedException, IOException, InferException {
InferResults inferResults = InferRunner.runInferObjC(inferCmd); InferResults inferResults = InferRunner.runInferObjC(inferCmd);
assertThat( assertThat(
"Results should not contain memory leak", "Results should contain memory leaks",
inferResults, inferResults,
doesNotContain( containsExactly(
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(
MEMORY_LEAK, MEMORY_LEAK,
memory_leak_file, 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"
)
);
}
} }

Loading…
Cancel
Save