diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index 741142358..21e65f9ed 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -279,6 +279,11 @@ OPTIONS Activates: Enable cost and disable all other checkers (Conversely: --no-cost-only) See also infer-analyze(1). + --cost-tests-only-autoreleasepool + Activates: [EXPERIMENTAL] Report only autoreleasepool size results + in cost tests (Conversely: --no-cost-tests-only-autoreleasepool) + See also infer-report(1) and infer-reportdiff(1). + --costs-current path Costs report of the latest revision See also infer-reportdiff(1). diff --git a/infer/man/man1/infer-report.txt b/infer/man/man1/infer-report.txt index 29445bf86..8fbda9c7c 100644 --- a/infer/man/man1/infer-report.txt +++ b/infer/man/man1/infer-report.txt @@ -39,6 +39,10 @@ OPTIONS Write a list of cost issues in a format suitable for cost tests to file + --cost-tests-only-autoreleasepool + Activates: [EXPERIMENTAL] Report only autoreleasepool size results + in cost tests (Conversely: --no-cost-tests-only-autoreleasepool) + --debug,-g Activates: Debug mode (also sets --debug-level 2, --developer-mode, --print-buckets, --print-types, diff --git a/infer/man/man1/infer-reportdiff.txt b/infer/man/man1/infer-reportdiff.txt index 8f98406ad..5e3ef2a35 100644 --- a/infer/man/man1/infer-reportdiff.txt +++ b/infer/man/man1/infer-reportdiff.txt @@ -20,6 +20,10 @@ DESCRIPTION OPTIONS + --cost-tests-only-autoreleasepool + Activates: [EXPERIMENTAL] Report only autoreleasepool size results + in cost tests (Conversely: --no-cost-tests-only-autoreleasepool) + --costs-current path Costs report of the latest revision diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index d6bac54a7..78c56ab90 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -279,6 +279,11 @@ OPTIONS Activates: Enable cost and disable all other checkers (Conversely: --no-cost-only) See also infer-analyze(1). + --cost-tests-only-autoreleasepool + Activates: [EXPERIMENTAL] Report only autoreleasepool size results + in cost tests (Conversely: --no-cost-tests-only-autoreleasepool) + See also infer-report(1) and infer-reportdiff(1). + --costs-current path Costs report of the latest revision See also infer-reportdiff(1). diff --git a/infer/src/IR/BUILTINS.ml b/infer/src/IR/BUILTINS.ml index 08088b85a..be9193509 100644 --- a/infer/src/IR/BUILTINS.ml +++ b/infer/src/IR/BUILTINS.ml @@ -112,6 +112,10 @@ module type S = sig val nsArray_arrayWithObjectsCount : t + val objc_autorelease_pool_pop : t + + val objc_autorelease_pool_push : t + val objc_cpp_throw : t val pthread_create : t diff --git a/infer/src/IR/BuiltinDecl.ml b/infer/src/IR/BuiltinDecl.ml index 5e4411ab0..0a99caa7b 100644 --- a/infer/src/IR/BuiltinDecl.ml +++ b/infer/src/IR/BuiltinDecl.ml @@ -155,6 +155,10 @@ let nsArray_arrayWithObjectsCount = create_objc_class_method "NSArray" "arrayWithObjects:count:" [None; None] +let objc_autorelease_pool_pop = create_procname "_objc_autoreleasePoolPop" + +let objc_autorelease_pool_push = create_procname "_objc_autoreleasePoolPush" + let objc_cpp_throw = create_procname "__infer_objc_cpp_throw" let pthread_create = create_procname "pthread_create" diff --git a/infer/src/IR/Procdesc.ml b/infer/src/IR/Procdesc.ml index 44323600d..3cc38c74f 100644 --- a/infer/src/IR/Procdesc.ml +++ b/infer/src/IR/Procdesc.ml @@ -131,7 +131,9 @@ module Node = struct ; loc: Location.t (** location in the source code *) ; mutable preds: t list (** predecessor nodes in the cfg *) ; pname: Procname.t (** name of the procedure the node belongs to *) - ; mutable succs: t list (** successor nodes in the cfg *) } + ; mutable succs: t list (** successor nodes in the cfg *) + ; mutable code_block_exit: t option + (** exit node corresponding to start node in a code block *) } let exn_handler_kind = Stmt_node ExceptionHandler @@ -149,7 +151,8 @@ module Node = struct ; pname ; succs= [] ; preds= [] - ; exn= [] } + ; exn= [] + ; code_block_exit= None } let compare node1 node2 = Int.compare node1.id node2.id @@ -401,6 +404,10 @@ module Node = struct F.asprintf "%s@\n%a" str_kind (Instrs.pp pe) (get_instrs node) + let set_code_block_exit node ~code_block_exit = node.code_block_exit <- Some code_block_exit + + let get_code_block_exit node = node.code_block_exit + (** simple key for a node: just look at the instructions *) let simple_key node = let add_instr instr = @@ -631,7 +638,8 @@ let create_node_from_not_reversed pdesc loc kind instrs = ; preds= [] ; pname= pdesc.attributes.proc_name ; succs= [] - ; exn= [] } + ; exn= [] + ; code_block_exit= None } in pdesc.nodes <- node :: pdesc.nodes ; node diff --git a/infer/src/IR/Procdesc.mli b/infer/src/IR/Procdesc.mli index d876e4a52..dbcbcf2fe 100644 --- a/infer/src/IR/Procdesc.mli +++ b/infer/src/IR/Procdesc.mli @@ -162,6 +162,13 @@ module Node : sig val get_wto_index : t -> int + val set_code_block_exit : t -> code_block_exit:t -> unit + (** Set an exit node corresponding to a start node of a code block. Using this, when there is a + code block, frontend can keep the correspondence between start/exit nodes of a code block. *) + + val get_code_block_exit : t -> t option + (** Get an exit node corresponding to a start node of a code block. *) + val is_dangling : t -> bool (** Returns true if the node is dangling, i.e. no successors and predecessors *) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 1bd7e557d..340bbc213 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -944,6 +944,12 @@ and costs_previous = "Costs report of the base revision to use for comparison" +and cost_tests_only_autoreleasepool = + CLOpt.mk_bool ~long:"cost-tests-only-autoreleasepool" + ~in_help:InferCommand.[(Report, manual_generic); (ReportDiff, manual_generic)] + "[EXPERIMENTAL] Report only autoreleasepool size results in cost tests" + + and siof_check_iostreams = CLOpt.mk_bool ~long:"siof-check-iostreams" ~in_help:InferCommand.[(Analyze, manual_siof)] @@ -2713,6 +2719,8 @@ and cost_scuba_logging = !cost_scuba_logging and costs_previous = !costs_previous +and cost_tests_only_autoreleasepool = !cost_tests_only_autoreleasepool + and cxx = !cxx and cxx_scope_guards = !cxx_scope_guards diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 127b7c9c8..25e802d93 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -232,6 +232,8 @@ val cost_scuba_logging : bool val costs_previous : string option +val cost_tests_only_autoreleasepool : bool + val cxx : bool val cxx_scope_guards : Yojson.Basic.t diff --git a/infer/src/base/CostIssuesTestField.ml b/infer/src/base/CostIssuesTestField.ml index fa600c5a4..56c3e06b1 100644 --- a/infer/src/base/CostIssuesTestField.ml +++ b/infer/src/base/CostIssuesTestField.ml @@ -7,6 +7,11 @@ open! IStd -type t = IsOnUIThread | Procedure | File | Cost | Trace [@@deriving equal] +type t = IsOnUIThread | Procedure | File | Cost | AutoreleasepoolSize | Trace [@@deriving equal] -let all_fields = [File; Procedure; Cost; IsOnUIThread; Trace] +let all_fields = + [ File + ; Procedure + ; (if Config.cost_tests_only_autoreleasepool then AutoreleasepoolSize else Cost) + ; IsOnUIThread + ; Trace ] diff --git a/infer/src/base/CostIssuesTestField.mli b/infer/src/base/CostIssuesTestField.mli index 02a47697b..c16577a57 100644 --- a/infer/src/base/CostIssuesTestField.mli +++ b/infer/src/base/CostIssuesTestField.mli @@ -7,6 +7,6 @@ open! IStd -type t = IsOnUIThread | Procedure | File | Cost | Trace [@@deriving equal] +type t = IsOnUIThread | Procedure | File | Cost | AutoreleasepoolSize | Trace [@@deriving equal] val all_fields : t list diff --git a/infer/src/biabduction/BuiltinDefn.ml b/infer/src/biabduction/BuiltinDefn.ml index 67bfc4d55..246a52e11 100644 --- a/infer/src/biabduction/BuiltinDefn.ml +++ b/infer/src/biabduction/BuiltinDefn.ml @@ -960,6 +960,12 @@ let nsArray_arrayWithObjectsCount = Builtin.register BuiltinDecl.nsArray_arrayWithObjectsCount execute_skip +let objc_autorelease_pool_pop = Builtin.register BuiltinDecl.objc_autorelease_pool_pop execute_skip + +let objc_autorelease_pool_push = + Builtin.register BuiltinDecl.objc_autorelease_pool_push execute_skip + + (* model throwing exception in objc/c++ as divergence *) let objc_cpp_throw = Builtin.register BuiltinDecl.objc_cpp_throw execute_exit diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 94b91c3c7..2020ce1fb 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -87,10 +87,6 @@ let nsobject_cl = "NSObject" let nsstring_cl = "NSString" -let objc_autorelease_pool_pop = "_objc_autoreleasePoolPop" - -let objc_autorelease_pool_push = "_objc_autoreleasePoolPush" - let objc_class = "objc_class" let objc_object = "objc_object" diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 4a45dea75..547fa0aee 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -81,10 +81,6 @@ val nsobject_cl : string val nsstring_cl : string -val objc_autorelease_pool_pop : string - -val objc_autorelease_pool_push : string - val objc_class : string val objc_object : string diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index f25cbf839..1e8ea3d6b 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -3119,14 +3119,14 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s CLocation.location_of_stmt_info trans_state.context.translation_unit_context.source_file stmt_info in - let mk_call_node fname = + let mk_call_node pname = let ret = mk_fresh_void_id_typ () in - let pname = Procname.from_string_c_fun fname in let instr = Sil.Call (ret, Const (Cfun pname), [], location, CallFlags.default) in - Procdesc.create_node procdesc location (Stmt_node (Call fname)) [instr] + Procdesc.create_node procdesc location (Stmt_node (Call (Procname.to_string pname))) [instr] in - let push_node = mk_call_node CFrontend_config.objc_autorelease_pool_push in - let pop_node = mk_call_node CFrontend_config.objc_autorelease_pool_pop in + let push_node = mk_call_node BuiltinDecl.objc_autorelease_pool_push in + let pop_node = mk_call_node BuiltinDecl.objc_autorelease_pool_pop in + Procdesc.Node.set_code_block_exit push_node ~code_block_exit:pop_node ; let res_trans_body = compoundStmt_trans {trans_state with succ_nodes= [pop_node]} stmts in Procdesc.set_succs push_node ~normal:(Some res_trans_body.control.root_nodes) ~exn:None ; Procdesc.set_succs pop_node ~normal:(Some trans_state.succ_nodes) ~exn:None ; diff --git a/infer/src/cost/cost.ml b/infer/src/cost/cost.ml index 181590f45..466f390b1 100644 --- a/infer/src/cost/cost.ml +++ b/infer/src/cost/cost.ml @@ -51,6 +51,10 @@ module InstrBasicCostWithReason = struct List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f) + let is_autorelease_function callee_pname = + String.equal (Procname.get_method callee_pname) "autorelease" + + let get_instr_cost_record tenv extras instr_node instr = match instr with | Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), params, _, _) when Config.inclusive_cost @@ -105,6 +109,8 @@ module InstrBasicCostWithReason = struct in if is_allocation_function callee_pname then CostDomain.plus CostDomain.unit_cost_allocation operation_cost + else if is_autorelease_function callee_pname then + CostDomain.plus CostDomain.unit_cost_autoreleasepool_size operation_cost else operation_cost | Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) -> CostDomain.zero_record @@ -176,8 +182,15 @@ module WorstCaseCost = struct let compute tenv extras cfg = let init = CostDomain.zero_record in let cost = - InstrCFG.fold_nodes cfg ~init ~f:(fun acc pair -> - exec_node tenv extras pair |> CostDomain.plus acc ) + let nodes_in_autoreleasepool = CostUtils.get_nodes_in_autoreleasepool cfg in + InstrCFG.fold_nodes cfg ~init ~f:(fun acc ((node, _) as pair) -> + let cost = exec_node tenv extras pair in + let cost = + if Procdesc.NodeSet.mem node nodes_in_autoreleasepool then + CostDomain.set_autoreleasepool_size_zero cost + else cost + in + CostDomain.plus acc cost ) in Option.iter (CostDomain.get_operation_cost cost).top_pname_opt ~f:(fun top_pname -> ScubaLogging.cost_log_message ~label:"unmodeled_function_top_cost" diff --git a/infer/src/cost/costDomain.ml b/infer/src/cost/costDomain.ml index 2c4c9a47a..5d2b68e85 100644 --- a/infer/src/cost/costDomain.ml +++ b/infer/src/cost/costDomain.ml @@ -87,6 +87,10 @@ let add_top_pname_opt kind cost_record top_pname_opt = let get_operation_cost cost_record = get_cost_kind CostKind.OperationCost cost_record +let set_autoreleasepool_size_zero cost_record = + VariantCostMap.remove CostKind.AutoreleasepoolSize cost_record + + let map ~f cost_record = VariantCostMap.map f cost_record let zero_record = VariantCostMap.empty @@ -104,6 +108,10 @@ let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record +let unit_cost_autoreleasepool_size = + VariantCostMap.increment CostKind.AutoreleasepoolSize zero_record + + let of_operation_cost operation_cost = VariantCostMap.increase_by CostKind.OperationCost {cost= operation_cost; top_pname_opt= None} diff --git a/infer/src/cost/costDomain.mli b/infer/src/cost/costDomain.mli index 822199ec1..fe959f185 100644 --- a/infer/src/cost/costDomain.mli +++ b/infer/src/cost/costDomain.mli @@ -62,6 +62,8 @@ val add_top_pname_opt : CostKind.t -> t -> Procname.t option -> t val get_operation_cost : t -> BasicCostWithReason.t +val set_autoreleasepool_size_zero : t -> t + val map : f:(BasicCostWithReason.t -> BasicCostWithReason.t) -> t -> t val zero_record : t @@ -79,6 +81,9 @@ val unit_cost_atomic_operation : t val unit_cost_allocation : t (** Map representing cost record \{OperationCost:0; AllocationCost:1; AutoreleasepoolSize:0\} *) +val unit_cost_autoreleasepool_size : t +(** Map representing cost record \{OperationCost:0; AllocationCost:0; AutoreleasepoolSize:1\} *) + val of_operation_cost : BasicCost.t -> t (** Map representing cost record \{OperationCost:operation_cost; AllocationCost:0; AutoreleasepoolSize:0\} *) diff --git a/infer/src/cost/costUtils.ml b/infer/src/cost/costUtils.ml index 9e1e91882..a1ee96d56 100644 --- a/infer/src/cost/costUtils.ml +++ b/infer/src/cost/costUtils.ml @@ -6,6 +6,7 @@ *) open! IStd +module L = Logging module BasicCost = CostDomain.BasicCost open BufferOverrunUtils.ModelEnv @@ -63,3 +64,35 @@ end module CString : S = struct let length exp inferbo_mem = BufferOverrunSemantics.eval_string_len exp inferbo_mem end + +let get_nodes_in_block ~block_start ~block_exit = + let rec accum_nodes_in_block ~from acc = + match from with + | [] -> + acc + | x :: tl -> + if Procdesc.Node.equal x block_exit || Procdesc.NodeSet.mem x acc then + accum_nodes_in_block ~from:tl acc + else + let from = Procdesc.Node.get_succs x @ tl in + accum_nodes_in_block ~from (Procdesc.NodeSet.add x acc) + in + accum_nodes_in_block ~from:[block_start] Procdesc.NodeSet.empty + + +let get_nodes_in_autoreleasepool cfg = + Procdesc.fold_instrs cfg ~init:Procdesc.NodeSet.empty ~f:(fun acc node -> function + | Sil.Call (_, Const (Cfun pname), _, _, _) + when Procname.equal pname BuiltinDecl.objc_autorelease_pool_push -> ( + match Procdesc.Node.get_code_block_exit node with + | None -> + (* Each _objc_autoreleasePoolPush has a corresponding _objc_autoreleasePoolPop, so we + should always have a corresponding exit node of the autoreleasepool block. *) + L.internal_error + "Not found: block-exit node matching to block_start node(%a) is not found.@." + Procdesc.Node.pp node ; + assert false + | Some block_exit -> + get_nodes_in_block ~block_start:node ~block_exit |> Procdesc.NodeSet.union acc ) + | _ -> + acc ) diff --git a/infer/src/integration/CostIssuesTest.ml b/infer/src/integration/CostIssuesTest.ml index ac2424d52..0915109ed 100644 --- a/infer/src/integration/CostIssuesTest.ml +++ b/infer/src/integration/CostIssuesTest.ml @@ -20,10 +20,17 @@ let pp_custom_of_cost_report fmt report cost_fields = F.fprintf fmt "%s%s" (comma_separator index) cost_item.loc.file | Cost -> F.fprintf fmt "%s%s" (comma_separator index) cost_item.exec_cost.hum.hum_polynomial + | AutoreleasepoolSize -> + F.fprintf fmt "%s%s" (comma_separator index) + cost_item.autoreleasepool_size.hum.hum_polynomial | IsOnUIThread -> F.fprintf fmt "%s OnUIThread:%b" (comma_separator index) cost_item.is_on_ui_thread | Trace -> - IssuesTest.pp_trace fmt cost_item.exec_cost.trace (comma_separator index) + let trace = + if Config.cost_tests_only_autoreleasepool then cost_item.autoreleasepool_size.trace + else cost_item.exec_cost.trace + in + IssuesTest.pp_trace fmt trace (comma_separator index) in List.iteri ~f:pp_cost_field cost_fields ; F.fprintf fmt "@." @@ -36,12 +43,14 @@ let cost_tests_jsonbug_compare (cost1 : Jsonbug_t.cost_item) (cost2 : Jsonbug_t. [%compare: string * string * string * Caml.Digest.t * bool] ( cost1.loc.file , cost1.procedure_id - , cost1.exec_cost.hum.hum_polynomial + , ( if Config.cost_tests_only_autoreleasepool then cost1.autoreleasepool_size.hum.hum_polynomial + else cost1.exec_cost.hum.hum_polynomial ) , cost1.hash , cost1.is_on_ui_thread ) ( cost2.loc.file , cost2.procedure_id - , cost2.exec_cost.hum.hum_polynomial + , ( if Config.cost_tests_only_autoreleasepool then cost2.autoreleasepool_size.hum.hum_polynomial + else cost2.exec_cost.hum.hum_polynomial ) , cost2.hash , cost2.is_on_ui_thread ) diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/Makefile b/infer/tests/codetoanalyze/objc/autoreleasepool/Makefile new file mode 100644 index 000000000..16634c4bd --- /dev/null +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/Makefile @@ -0,0 +1,19 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +TESTS_DIR = ../../.. + +CLANG_OPTIONS = -c $(OBJC_CLANG_OPTIONS) +INFER_OPTIONS = --cost-only --debug-exceptions --project-root $(TESTS_DIR) +INFERPRINT_OPTIONS = --issues-tests +INFERPRINT_COST_OPTIONS = --cost-tests-only-autoreleasepool --cost-issues-tests + +SOURCES = $(wildcard *.m) + +include $(TESTS_DIR)/clang.make +include $(TESTS_DIR)/objc.make +include $(TESTS_DIR)/cost.make + +infer-out/report.json: $(MAKEFILE_LIST) diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/basic.m b/infer/tests/codetoanalyze/objc/autoreleasepool/basic.m new file mode 100644 index 000000000..62294fc92 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/basic.m @@ -0,0 +1,96 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#import + +@interface Basic : NSObject +@end + +@implementation Basic + +- (void)call_autorelease_constant { + NSString* x = [[NSString alloc] initWith:"test"]; + return [x autorelease]; +} + +- (void)autoreleased_in_loop_linear:(int)n { + for (int i = 0; i < n; i++) { + [self call_autorelease_constant]; + } +} + +- (void)autoreleased_in_autoreleasepool_zero:(int)n { + for (int i = 0; i < n; i++) { + @autoreleasepool { + [self call_autorelease_constant]; + } + } +} + +/* This function is problematic because autoreleased objects inside the loop + will not be released until the loop terminates. However, the checker is not + measuring peak memory but size of autoreleasepool increased by the function. + Since we wrap the loop inside the autoreleasepool block, we will be setting + the size to 0 for the whole function. */ +- (void)loop_in_autoreleasepool_zero:(int)n { + @autoreleasepool { + for (int i = 0; i < n; i++) { + [self call_autorelease_constant]; + } + } +} + +- (void)autoreleased_in_loop_nested_zero:(int)n { + @autoreleasepool { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + [self call_autorelease_constant]; + } + @autoreleasepool { + [self call_autorelease_constant]; + } + } + } +} + +- (void)autoreleased_in_loop_sequential_constant:(int)n { + @autoreleasepool { + for (int i = 0; i < n; i++) { + [self call_autorelease_constant]; + } + } + [self call_autorelease_constant]; + @autoreleasepool { + for (int i = 0; i < n; i++) { + [self call_autorelease_constant]; + } + } +} + +- (void)autoreleased_in_loop_sequential_linear:(int)n { + @autoreleasepool { + [self call_autorelease_constant]; + } + for (int i = 0; i < n; i++) { + [self call_autorelease_constant]; + } + @autoreleasepool { + [self call_autorelease_constant]; + } +} + +- (void)call_no_autorelease_zero { + NSString* x = [[NSString alloc] initWith:"test"]; + return x; +} + +- (void)no_autoreleased_in_loop_zero:(int)n { + for (int i = 0; i < n; i++) { + [self call_no_autorelease]; + } +} + +@end diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp new file mode 100644 index 000000000..22df3c619 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/autoreleasepool/cost-issues.exp @@ -0,0 +1,10 @@ +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_autoreleasepool_zero:, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, n, OnUIThread:false, [{n},Loop] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_nested_zero:, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_constant:, 1, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, n, OnUIThread:false, [{n},Loop] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_autorelease_constant, 1, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_no_autorelease_zero, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.dealloc, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.loop_in_autoreleasepool_zero:, 0, OnUIThread:false, [] +codetoanalyze/objc/autoreleasepool/basic.m, Basic.no_autoreleased_in_loop_zero:, 0, OnUIThread:false, [] diff --git a/infer/tests/codetoanalyze/objc/autoreleasepool/issues.exp b/infer/tests/codetoanalyze/objc/autoreleasepool/issues.exp new file mode 100644 index 000000000..e69de29bb