diff --git a/infer/src/bufferoverrun/bufferOverrunSemantics.ml b/infer/src/bufferoverrun/bufferOverrunSemantics.ml index 7795e9625..f4045af32 100644 --- a/infer/src/bufferoverrun/bufferOverrunSemantics.ml +++ b/infer/src/bufferoverrun/bufferOverrunSemantics.ml @@ -502,14 +502,9 @@ let mk_eval_sym_trace ?(is_params_ref = false) integer_type_widths ; trace_of_sym } -let mk_eval_sym_mode ~mode integer_type_widths callee_formals actual_exps caller_mem = - let eval_sym_trace = - mk_eval_sym_trace integer_type_widths callee_formals actual_exps caller_mem ~mode - in - eval_sym_trace.eval_sym - +let mk_eval_sym_cost integer_type_widths callee_formals actual_exps caller_mem = + mk_eval_sym_trace integer_type_widths callee_formals actual_exps caller_mem ~mode:EvalCost -let mk_eval_sym_cost = mk_eval_sym_mode ~mode:EvalCost (* This function evaluates the array length conservatively, which is useful when there are multiple array locations and their lengths are joined to top. For example, if the [arr_locs] points to diff --git a/infer/src/bufferoverrun/bufferOverrunSemantics.mli b/infer/src/bufferoverrun/bufferOverrunSemantics.mli index c6bd84ce6..944cecaac 100644 --- a/infer/src/bufferoverrun/bufferOverrunSemantics.mli +++ b/infer/src/bufferoverrun/bufferOverrunSemantics.mli @@ -79,7 +79,7 @@ val mk_eval_sym_cost : -> (Pvar.t * Typ.t) list -> (Exp.t * Typ.t) list -> BufferOverrunDomain.Mem.t - -> Bounds.Bound.eval_sym + -> BufferOverrunDomain.eval_sym_trace (** Make [eval_sym] function of [EvalCost] mode for on-demand symbol evaluation *) module Prune : sig diff --git a/infer/src/bufferoverrun/polynomials.ml b/infer/src/bufferoverrun/polynomials.ml index 58d488285..63e90ac30 100644 --- a/infer/src/bufferoverrun/polynomials.ml +++ b/infer/src/bufferoverrun/polynomials.ml @@ -105,9 +105,16 @@ module NonNegativeBoundWithDegreeKind = struct let make_err_trace_symbol symbol = NonNegativeBound.make_err_trace symbol end +let pp_magic_parentheses pp fmt x = + let s = F.asprintf "%a" pp x in + if String.contains s ' ' then F.fprintf fmt "(%s)" s else F.pp_print_string fmt s + + module NonNegativeNonTopPolynomial = struct module Key = struct - type t = NonNegativeBoundWithDegreeKind of NonNegativeBoundWithDegreeKind.t + type t = + | NonNegativeBoundWithDegreeKind of NonNegativeBoundWithDegreeKind.t + | FuncPtr of Symb.SymbolPath.partial [@@deriving compare] let lift_valclass = function @@ -115,10 +122,20 @@ module NonNegativeNonTopPolynomial = struct Symbolic (NonNegativeBoundWithDegreeKind s) | (Constant _ | ValTop _) as x -> x + + + let pp_hum ~hum f = function + | NonNegativeBoundWithDegreeKind bound -> + pp_magic_parentheses (NonNegativeBoundWithDegreeKind.pp ~hum) f bound + | FuncPtr partial -> + F.fprintf f "|%a|" Symb.SymbolPath.pp_partial partial + + + let pp = pp_hum ~hum:true end module M = struct - include Caml.Map.Make (Key) + include PrettyPrintable.MakePPMap (Key) let increasing_union ~f m1 m2 = union (fun _ v1 v2 -> Some (f v1 v2)) m1 m2 @@ -195,12 +212,15 @@ module NonNegativeNonTopPolynomial = struct let rec degree_poly {terms} = M.fold (fun t p cur_max -> - match t with - | NonNegativeBoundWithDegreeKind t -> - let degree_term = - Degree.succ (NonNegativeBoundWithDegreeKind.degree_kind t) (degree_poly p) - in - if Degree.compare degree_term cur_max > 0 then degree_term else cur_max ) + let p = degree_poly p in + let degree_term = + match t with + | NonNegativeBoundWithDegreeKind t -> + Degree.succ (NonNegativeBoundWithDegreeKind.degree_kind t) p + | FuncPtr _ -> + p + in + if Degree.compare degree_term cur_max > 0 then degree_term else cur_max ) terms Degree.zero @@ -293,8 +313,6 @@ module NonNegativeNonTopPolynomial = struct {const= NonNegativeInt.zero; terms} - let mult_symb : t -> Key.t -> t = fun x s -> {x with poly= mult_symb_poly x.poly s} - let rec mult_poly : poly -> poly -> poly = fun p1 p2 -> if is_zero_poly p1 || is_zero_poly p2 then zero_poly @@ -324,6 +342,13 @@ module NonNegativeNonTopPolynomial = struct body ) + let singleton key = + { poly= {const= NonNegativeInt.zero; terms= M.singleton key one_poly} + ; autoreleasepool_trace= None } + + + let of_func_ptr path = singleton (FuncPtr path) + let rec of_valclass : (NonNegativeInt.t, Key.t, 't) valclass -> ('t, t, 't) below_above = function | ValTop trace -> Above trace @@ -332,9 +357,7 @@ module NonNegativeNonTopPolynomial = struct | Symbolic (NonNegativeBoundWithDegreeKind s as key) -> ( match NonNegativeBoundWithDegreeKind.split_mult s with | None -> - Val - { poly= {const= NonNegativeInt.zero; terms= M.singleton key one_poly} - ; autoreleasepool_trace= None } + Val (singleton key) | Some (s1, s2) -> ( match ( of_valclass (Key.lift_valclass (NonNegativeBoundWithDegreeKind.classify s1)) @@ -346,6 +369,8 @@ module NonNegativeNonTopPolynomial = struct assert false | (Above _ as t), _ | _, (Above _ as t) -> t ) ) + | Symbolic (FuncPtr _ as key) -> + Val (singleton key) let rec int_lb {const; terms} = @@ -355,7 +380,9 @@ module NonNegativeNonTopPolynomial = struct | NonNegativeBoundWithDegreeKind symbol -> let s_lb = NonNegativeBoundWithDegreeKind.int_lb symbol in let p_lb = int_lb polynomial in - NonNegativeInt.((s_lb * p_lb) + acc) ) + NonNegativeInt.((s_lb * p_lb) + acc) + | FuncPtr _ -> + acc ) terms const @@ -367,7 +394,9 @@ module NonNegativeNonTopPolynomial = struct Option.bind acc ~f:(fun acc -> Option.bind (NonNegativeBoundWithDegreeKind.int_ub symbol) ~f:(fun s_ub -> Option.map (int_ub polynomial) ~f:(fun p_ub -> - NonNegativeInt.((s_ub * p_ub) + acc) ) ) ) ) + NonNegativeInt.((s_ub * p_ub) + acc) ) ) ) + | FuncPtr _ -> + acc ) terms (Some const) @@ -398,9 +427,9 @@ module NonNegativeNonTopPolynomial = struct ; terms= M.fold (fun s p acc -> + let p' = mask_min_max_constant_poly p in match s with | NonNegativeBoundWithDegreeKind s -> - let p' = mask_min_max_constant_poly p in M.update (NonNegativeBoundWithDegreeKind (NonNegativeBoundWithDegreeKind.mask_min_max_constant s)) @@ -409,7 +438,9 @@ module NonNegativeNonTopPolynomial = struct Some p' | Some p -> if leq_poly ~lhs:p ~rhs:p' then Some p' else Some p ) - acc ) + acc + | FuncPtr _ as key -> + M.add key p' acc ) terms M.empty } @@ -428,10 +459,11 @@ module NonNegativeNonTopPolynomial = struct if is_constant p1 then p1 else if is_constant p2 then p2 else p1 - let subst callee_pname location = + let subst callee_pname location {poly; autoreleasepool_trace} eval_sym eval_func_ptrs + get_closure_callee_cost ~default_closure_cost = let exception ReturnTop of (NonNegativeBoundWithDegreeKind.t * BoundTrace.t) in (* avoids top-lifting everything *) - let rec subst_poly {const; terms} eval_sym = + let rec subst_poly {const; terms} = M.fold (fun s p acc -> match s with @@ -442,46 +474,84 @@ module NonNegativeNonTopPolynomial = struct | None -> acc | Some c -> - let p = subst_poly p eval_sym in + let p = subst_poly p in mult_const_positive p c |> plus_poly acc ) | ValTop trace -> - let p = subst_poly p eval_sym in + let p = subst_poly p in if is_zero_poly p then acc else raise (ReturnTop (s, trace)) | Symbolic s -> - let p = subst_poly p eval_sym in - mult_symb_poly p (NonNegativeBoundWithDegreeKind s) |> plus_poly acc ) ) + let p = subst_poly p in + mult_symb_poly p (NonNegativeBoundWithDegreeKind s) |> plus_poly acc ) + | FuncPtr s -> + let funcptr_p = + let p = subst_poly p in + match FuncPtr.Set.is_singleton_or_more (eval_func_ptrs s) with + | Singleton (Closure {name}) -> + let closure_p = + match get_closure_callee_cost name with + | Some {poly= closure_p} -> + closure_p + | None -> + poly_of_non_negative_int default_closure_cost + in + mult_poly closure_p p + | Singleton (Path path) -> + mult_symb_poly p (FuncPtr path) + | Empty | More -> + mult_poly (poly_of_non_negative_int default_closure_cost) p + in + plus_poly acc funcptr_p ) terms (poly_of_non_negative_int const) in - fun {poly; autoreleasepool_trace} eval_sym -> - match subst_poly poly eval_sym with - | poly -> - let autoreleasepool_trace = - Option.map autoreleasepool_trace ~f:(BoundTrace.call ~callee_pname ~location) - in - Val {poly; autoreleasepool_trace} - | exception ReturnTop s_trace -> - Above s_trace + match subst_poly poly with + | poly -> + let autoreleasepool_trace = + Option.map autoreleasepool_trace ~f:(BoundTrace.call ~callee_pname ~location) + in + Val {poly; autoreleasepool_trace} + | exception ReturnTop s_trace -> + Above s_trace (** Emit a pair (d,t) where d is the degree of the polynomial and t is the first term with such - degree *) + degree. When calculating the degree, it ignores symbols of function pointer, so they are + addressed as if zero cost. *) let degree_with_term {poly; autoreleasepool_trace} = - let rec degree_with_term_poly {terms} = - M.fold - (fun t p cur_max -> - match t with - | NonNegativeBoundWithDegreeKind t -> - let d, p' = degree_with_term_poly p in - let degree_term = - ( Degree.succ (NonNegativeBoundWithDegreeKind.degree_kind t) d - , mult_symb p' (NonNegativeBoundWithDegreeKind t) ) - in - if [%compare: Degree.t * t] degree_term cur_max > 0 then degree_term else cur_max ) - terms - (Degree.zero, one ()) + let rec is_func_ptr_poly {const; terms} = + NonNegativeInt.is_zero const + && M.for_all + (fun key v -> + match key with + | FuncPtr _ -> + true + | NonNegativeBoundWithDegreeKind _ -> + is_func_ptr_poly v ) + terms + in + let rec degree_with_term_poly ({terms} as poly) = + if is_func_ptr_poly poly then (Degree.zero, true, poly) + else + M.fold + (fun t p cur_max -> + let degree_term = + match (t, degree_with_term_poly p) with + | NonNegativeBoundWithDegreeKind b, (d, false, p') -> + ( Degree.succ (NonNegativeBoundWithDegreeKind.degree_kind b) d + , false + , mult_symb_poly p' t ) + | FuncPtr _, (_, _, p') | _, (_, true, p') -> + (* It ignores function pointers when calculating degree of polynomial, since their + semantics is different to the other symbolic values. For example, when a + function has a complexity of |fptr| where fptr is a function pointer, it does + not make sense to say the function has a linear complexity. *) + (Degree.zero, true, mult_symb_poly p' t) + in + if [%compare: Degree.t * bool * poly] degree_term cur_max > 0 then degree_term + else cur_max ) + terms (Degree.zero, false, one_poly) in - let d, p = degree_with_term_poly poly in - (d, {p with autoreleasepool_trace}) + let d, _, poly = degree_with_term_poly poly in + (d, {poly; autoreleasepool_trace}) let degree p = fst (degree_with_term p) @@ -500,16 +570,7 @@ module NonNegativeNonTopPolynomial = struct let pp_exp fmt (e : PositiveInt.t) = if Z.(gt (e :> Z.t) one) then PositiveInt.pp_exponent fmt e in - let pp_magic_parentheses pp fmt x = - let s = F.asprintf "%a" pp x in - if String.contains s ' ' then F.fprintf fmt "(%s)" s else F.pp_print_string fmt s - in - let pp_symb ~hum fmt symb = - match (symb : Key.t) with - | NonNegativeBoundWithDegreeKind symb -> - pp_magic_parentheses (NonNegativeBoundWithDegreeKind.pp ~hum) fmt symb - in - let pp_symb_exp ~hum fmt (symb, exp) = F.fprintf fmt "%a%a" (pp_symb ~hum) symb pp_exp exp in + let pp_symb_exp ~hum fmt (symb, exp) = F.fprintf fmt "%a%a" (Key.pp_hum ~hum) symb pp_exp exp in let pp_symbs ~hum fmt (last, others) = List.rev_append others [last] |> Pp.seq ~sep:multiplication_sep (pp_symb_exp ~hum) fmt in @@ -547,9 +608,14 @@ module NonNegativeNonTopPolynomial = struct let rec get_symbols_sub {terms} acc = M.fold (fun s p acc -> - match s with - | NonNegativeBoundWithDegreeKind s -> - get_symbols_sub p (NonNegativeBoundWithDegreeKind.symbol s :: acc) ) + let acc = + match s with + | NonNegativeBoundWithDegreeKind s -> + NonNegativeBoundWithDegreeKind.symbol s :: acc + | FuncPtr _ -> + acc + in + get_symbols_sub p acc ) terms acc in get_symbols_sub p.poly [] @@ -738,6 +804,8 @@ module NonNegativePolynomial = struct |> make_trace_set ~map_above:TopTrace.unbounded_loop + let of_func_ptr path = Val (NonNegativeNonTopPolynomial.of_func_ptr path) + let is_symbolic = function | Below _ | Above _ -> false @@ -783,7 +851,8 @@ module NonNegativePolynomial = struct ~f_below:UnreachableTraces.join - let subst callee_pname location p eval_sym = + let subst callee_pname location p eval_sym eval_func_ptrs get_closure_callee_cost + ~default_closure_cost = match p with | Above callee_traces -> Above @@ -796,7 +865,16 @@ module NonNegativePolynomial = struct (fun callee_trace -> UnreachableTrace.call ~callee_pname ~location callee_trace) callee_traces) | Val p -> - NonNegativeNonTopPolynomial.subst callee_pname location p eval_sym + let get_closure_callee_cost pname = + match get_closure_callee_cost pname with + | Some (Val p) -> + Some p + | None | Some (Below _ | Above _) -> + (* It doesn't propagate Top/Bottoms if the closure has these costs. *) + None + in + NonNegativeNonTopPolynomial.subst callee_pname location p eval_sym eval_func_ptrs + get_closure_callee_cost ~default_closure_cost |> make_trace_set ~map_above:(fun (symbol, bound_trace) -> TopTrace.unbounded_symbol ~location ~symbol bound_trace ) diff --git a/infer/src/bufferoverrun/polynomials.mli b/infer/src/bufferoverrun/polynomials.mli index fa390059e..fe7a97e9c 100644 --- a/infer/src/bufferoverrun/polynomials.mli +++ b/infer/src/bufferoverrun/polynomials.mli @@ -71,6 +71,8 @@ module NonNegativePolynomial : sig val of_non_negative_bound : ?degree_kind:DegreeKind.t -> Bounds.NonNegativeBound.t -> t + val of_func_ptr : Symb.SymbolPath.partial -> t + val plus : t -> t -> t val mult_unreachable : t -> t -> t @@ -82,7 +84,15 @@ module NonNegativePolynomial : sig val min_default_left : t -> t -> t - val subst : Procname.t -> Location.t -> t -> Bounds.Bound.eval_sym -> t + val subst : + Procname.t + -> Location.t + -> t + -> Bounds.Bound.eval_sym + -> FuncPtr.Set.eval_func_ptrs + -> (Procname.t -> t option) + -> default_closure_cost:Ints.NonNegativeInt.t + -> t val degree : t -> Degree.t option diff --git a/infer/src/cost/cost.ml b/infer/src/cost/cost.ml index 9f9942fd7..745f3c5ea 100644 --- a/infer/src/cost/cost.ml +++ b/infer/src/cost/cost.ml @@ -25,13 +25,18 @@ type extras_WorstCaseCost = ; get_formals: Procname.t -> (Pvar.t * Typ.t) list option ; proc_resolve_attributes: Procname.t -> ProcAttributes.t option } -let instantiate_cost integer_type_widths ~inferbo_caller_mem ~callee_pname ~callee_formals ~params - ~callee_cost ~loc = - let eval_sym = +let instantiate_cost ?get_closure_callee_cost ~default_closure_cost integer_type_widths + ~inferbo_caller_mem ~callee_pname ~callee_formals ~params ~callee_cost ~loc = + let {BufferOverrunDomain.eval_sym; eval_func_ptrs} = BufferOverrunSemantics.mk_eval_sym_cost integer_type_widths callee_formals params inferbo_caller_mem in - BasicCostWithReason.subst callee_pname loc callee_cost eval_sym + let get_closure_callee_cost pname = + Option.bind get_closure_callee_cost ~f:(fun get_closure_callee_cost -> + get_closure_callee_cost pname ) + in + BasicCostWithReason.subst callee_pname loc callee_cost eval_sym eval_func_ptrs + get_closure_callee_cost ~default_closure_cost module InstrBasicCostWithReason = struct @@ -101,6 +106,21 @@ module InstrBasicCostWithReason = struct |> Option.value ~default:(Option.value callee_cost_opt ~default:BasicCostWithReason.zero) + let dispatch_autoreleasepool_func_ptr_call {inferbo_invariant_map; integer_type_widths} instr_node + fun_exp = + BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map + |> Option.value_map ~default:BasicCostWithReason.zero ~f:(fun inferbo_mem -> + let func_ptrs = + BufferOverrunSemantics.eval integer_type_widths fun_exp inferbo_mem + |> BufferOverrunDomain.Val.get_func_ptrs + in + match FuncPtr.Set.is_singleton_or_more func_ptrs with + | Singleton (Path path) -> + BasicCost.of_func_ptr path |> BasicCostWithReason.of_basic_cost + | _ -> + BasicCostWithReason.zero ) + + let get_instr_cost_record tenv extras cfg instr_node instr = match instr with | Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), params, location, _) @@ -126,12 +146,25 @@ module InstrBasicCostWithReason = struct integer_type_widths inferbo_get_summary ) in let get_callee_cost_opt kind inferbo_mem = + let default_closure_cost = + match (kind : CostKind.t) with + | OperationCost -> + Ints.NonNegativeInt.one + | AllocationCost | AutoreleasepoolSize -> + Ints.NonNegativeInt.zero + in match (get_summary callee_pname, get_formals callee_pname) with | Some {CostDomain.post= callee_cost_record}, Some callee_formals -> CostDomain.find_opt kind callee_cost_record |> Option.map ~f:(fun callee_cost -> - instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem - ~callee_pname ~callee_formals ~params ~callee_cost ~loc:location ) + let get_closure_callee_cost pname = + get_summary pname + |> Option.map ~f:(fun {CostDomain.post} -> + CostDomain.get_cost_kind kind post ) + in + instantiate_cost ~get_closure_callee_cost ~default_closure_cost + integer_type_widths ~inferbo_caller_mem:inferbo_mem ~callee_pname + ~callee_formals ~params ~callee_cost ~loc:location ) | _ -> None in @@ -152,12 +185,21 @@ module InstrBasicCostWithReason = struct model_env ret cfg location inferbo_mem ) ) | Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) -> CostDomain.zero_record + | Sil.Call (_, fun_exp, _, _location, _) -> + CostDomain.construct ~f:(fun kind -> + match kind with + | OperationCost -> + BasicCostWithReason.one () + | AllocationCost -> + BasicCostWithReason.zero + | AutoreleasepoolSize -> + dispatch_autoreleasepool_func_ptr_call extras instr_node fun_exp ) | Sil.Load {id= lhs_id} when Ident.is_none lhs_id -> (* dummy deref inserted by frontend--don't count as a step. In JDK 11, dummy deref disappears and causes cost differences otherwise. *) CostDomain.zero_record - | Sil.Load _ | Sil.Store _ | Sil.Call _ | Sil.Prune _ -> + | Sil.Load _ | Sil.Store _ | Sil.Prune _ -> CostDomain.unit_cost_atomic_operation | Sil.Metadata Skip -> ( match InstrCFG.Node.kind instr_node with diff --git a/infer/src/cost/cost.mli b/infer/src/cost/cost.mli index f4e365f52..8c6f4b71b 100644 --- a/infer/src/cost/cost.mli +++ b/infer/src/cost/cost.mli @@ -15,7 +15,9 @@ val checker : -> CostDomain.summary option val instantiate_cost : - Typ.IntegerWidths.t + ?get_closure_callee_cost:(Procname.t -> CostDomain.BasicCostWithReason.t option) + -> default_closure_cost:Ints.NonNegativeInt.t + -> Typ.IntegerWidths.t -> inferbo_caller_mem:BufferOverrunDomain.Mem.t -> callee_pname:Procname.t -> callee_formals:(Pvar.t * Typ.t) list diff --git a/infer/src/cost/costDomain.ml b/infer/src/cost/costDomain.ml index 3534feadd..2bfb61adb 100644 --- a/infer/src/cost/costDomain.ml +++ b/infer/src/cost/costDomain.ml @@ -33,8 +33,15 @@ module BasicCostWithReason = struct let of_basic_cost cost = {cost; top_pname_opt= None} - let subst callee_pname location {cost; top_pname_opt} eval_sym = - let cost = BasicCost.subst callee_pname location cost eval_sym in + let subst callee_pname location {cost; top_pname_opt} eval_sym eval_func_ptrs + get_closure_callee_cost ~default_closure_cost = + let get_closure_callee_cost pname = + get_closure_callee_cost pname |> Option.map ~f:(fun {cost} -> cost) + in + let cost = + BasicCost.subst callee_pname location cost eval_sym eval_func_ptrs get_closure_callee_cost + ~default_closure_cost + in if BasicCost.is_top cost then {cost; top_pname_opt= Some callee_pname} else {cost; top_pname_opt} diff --git a/infer/src/cost/costDomain.mli b/infer/src/cost/costDomain.mli index 9817c564c..d71c7895f 100644 --- a/infer/src/cost/costDomain.mli +++ b/infer/src/cost/costDomain.mli @@ -43,7 +43,15 @@ module BasicCostWithReason : sig val plus : t -> t -> t - val subst : Procname.t -> Location.t -> t -> Bounds.Bound.eval_sym -> t + val subst : + Procname.t + -> Location.t + -> t + -> Bounds.Bound.eval_sym + -> FuncPtr.Set.eval_func_ptrs + -> (Procname.t -> t option) + -> default_closure_cost:Ints.NonNegativeInt.t + -> t val degree : t -> Polynomials.Degree.t option diff --git a/infer/src/cost/hoisting.ml b/infer/src/cost/hoisting.ml index ee9cbf8b0..39150f468 100644 --- a/infer/src/cost/hoisting.ml +++ b/infer/src/cost/hoisting.ml @@ -113,8 +113,9 @@ let get_cost_if_expensive tenv integer_type_widths get_callee_cost_summary_and_f let callee_cost = CostDomain.get_operation_cost cost_record in if CostDomain.BasicCost.is_symbolic callee_cost.cost then Some - (Cost.instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem - ~callee_pname:pname ~callee_formals ~params ~callee_cost ~loc) + (Cost.instantiate_cost ~default_closure_cost:Ints.NonNegativeInt.one integer_type_widths + ~inferbo_caller_mem:inferbo_mem ~callee_pname:pname ~callee_formals ~params + ~callee_cost ~loc) .cost else None | None -> diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp index d04802891..70c525cfd 100644 --- a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp @@ -24,12 +24,12 @@ codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_line codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callMutableCopyObject_zero:x:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callNewObject_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.dealloc, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.callMyEnumerator_linear_FN:, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.callMyEnumerator_linear_FN:, 1, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.makeMyEnumerator_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_enumerator.m, MyEnumerator.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_enumerator.m, MyEnumerator.initWithArray:filter:, 0, OnUIThread:false, [] -codetoanalyze/objc/autoreleasepool/arc_enumerator.m, MyEnumerator.nextObject, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/arc_enumerator.m, MyEnumerator.nextObject, |self->_filter|, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_enumerator.m, objc_blockArcEnumerator.makeMyEnumerator_zero:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease] codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callInitForReadingFromData_constant:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.initForReadingFromData:error:] codetoanalyze/objc/autoreleasepool/arc_keyed_unarchiver.m, ArcKeyedUnarchiver.callInitForReadingWithData_constant:data:, 1, OnUIThread:false, [autorelease,Modeled call to NSKeyedUnarchiver.initForReadingWithData:]