From 74e16bdb58545e38bede54540c09db970b9e88c7 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Fri, 6 Apr 2018 05:21:13 -0700 Subject: [PATCH] [retain cycles] Define custom compare function that is more suited to dedup cycles Reviewed By: mbouaziz Differential Revision: D7517385 fbshipit-source-id: 00e0433 --- infer/src/biabduction/RetainCyclesType.ml | 54 +++++++++++++------ infer/src/biabduction/RetainCyclesType.mli | 6 +-- .../tests/codetoanalyze/objc/errors/Makefile | 1 + .../codetoanalyze/objc/errors/issues.exp | 1 + .../RetainCycleDeduplication.m | 37 +++++++++++++ 5 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleDeduplication.m diff --git a/infer/src/biabduction/RetainCyclesType.ml b/infer/src/biabduction/RetainCyclesType.ml index 2ef8a6212..b803ead1d 100644 --- a/infer/src/biabduction/RetainCyclesType.ml +++ b/infer/src/biabduction/RetainCyclesType.ml @@ -8,24 +8,48 @@ *) open! IStd -type retain_cycle_node = {rc_node_exp: Exp.t; rc_node_typ: Typ.t} [@@deriving compare] +type retain_cycle_node = {rc_node_exp: Exp.t; rc_node_typ: Typ.t} -type retain_cycle_field_objc = - {rc_field_name: Typ.Fieldname.t; rc_field_inst: Sil.inst} - [@@deriving compare] +type retain_cycle_field = {rc_field_name: Typ.Fieldname.t; rc_field_inst: Sil.inst} -type retain_cycle_edge_objc = - {rc_from: retain_cycle_node; rc_field: retain_cycle_field_objc} - [@@deriving compare] +type retain_cycle_edge_obj = {rc_from: retain_cycle_node; rc_field: retain_cycle_field} -type retain_cycle_edge = - | Object of retain_cycle_edge_objc - | Block of Typ.Procname.t * Pvar.t - [@@deriving compare] +type retain_cycle_edge = Object of retain_cycle_edge_obj | Block of Typ.Procname.t * Pvar.t -let retain_cycle_edge_equal = [%compare.equal : retain_cycle_edge] +type t = {rc_head: retain_cycle_edge; rc_elements: retain_cycle_edge list} + +let compare_retain_cycle_node (node1: retain_cycle_node) (node2: retain_cycle_node) = + Typ.compare node1.rc_node_typ node2.rc_node_typ + + +let compare_retain_cycle_field (node1: retain_cycle_field) (node2: retain_cycle_field) = + Typ.Fieldname.compare node1.rc_field_name node2.rc_field_name + + +let compare_retain_cycle_edge_obj (obj1: retain_cycle_edge_obj) (obj2: retain_cycle_edge_obj) = + let obj1_pair = Tuple.T2.create obj1.rc_from obj1.rc_field in + let obj2_pair = Tuple.T2.create obj2.rc_from obj2.rc_field in + Tuple.T2.compare ~cmp1:compare_retain_cycle_node ~cmp2:compare_retain_cycle_field obj1_pair + obj2_pair + + +let compare_retain_cycle_edge (edge1: retain_cycle_edge) (edge2: retain_cycle_edge) = + match (edge1, edge2) with + | Object edge_obj1, Object edge_obj2 -> + compare_retain_cycle_edge_obj edge_obj1 edge_obj2 + | Block (procname1, _), Block (procname2, _) -> + Typ.Procname.compare procname1 procname2 + | Object _, Block _ -> + 1 + | Block _, Object _ -> + -1 + + +let equal_retain_cycle_edge = [%compare.equal : retain_cycle_edge] + +let compare (rc1: t) (rc2: t) = + List.compare compare_retain_cycle_edge rc1.rc_elements rc2.rc_elements -type t = {rc_head: retain_cycle_edge; rc_elements: retain_cycle_edge list} [@@deriving compare] module Set = Caml.Set.Make (struct type nonrec t = t @@ -45,7 +69,7 @@ let _retain_cycle_node_to_string (node: retain_cycle_node) = Format.sprintf "%s : %s" (Exp.to_string node.rc_node_exp) (Typ.to_string node.rc_node_typ) -let retain_cycle_field_to_string (field: retain_cycle_field_objc) = +let retain_cycle_field_to_string (field: retain_cycle_field) = Format.sprintf "%s[%s]" (Typ.Fieldname.to_string field.rc_field_name) (Sil.inst_to_string field.rc_field_inst) @@ -76,7 +100,7 @@ let find_minimum_element cycle = let shift cycle head : t = let rec shift_elements rev_tail elements = match elements with - | hd :: rest when not (retain_cycle_edge_equal hd head) -> + | hd :: rest when not (equal_retain_cycle_edge hd head) -> shift_elements (hd :: rev_tail) rest | _ -> elements @ List.rev rev_tail diff --git a/infer/src/biabduction/RetainCyclesType.mli b/infer/src/biabduction/RetainCyclesType.mli index f3e64b13b..d7ee8d060 100644 --- a/infer/src/biabduction/RetainCyclesType.mli +++ b/infer/src/biabduction/RetainCyclesType.mli @@ -11,11 +11,11 @@ open! IStd type retain_cycle_node = {rc_node_exp: Exp.t; rc_node_typ: Typ.t} -type retain_cycle_field_objc = {rc_field_name: Typ.Fieldname.t; rc_field_inst: Sil.inst} +type retain_cycle_field = {rc_field_name: Typ.Fieldname.t; rc_field_inst: Sil.inst} -type retain_cycle_edge_objc = {rc_from: retain_cycle_node; rc_field: retain_cycle_field_objc} +type retain_cycle_edge_obj = {rc_from: retain_cycle_node; rc_field: retain_cycle_field} -type retain_cycle_edge = Object of retain_cycle_edge_objc | Block of Typ.Procname.t * Pvar.t +type retain_cycle_edge = Object of retain_cycle_edge_obj | Block of Typ.Procname.t * Pvar.t (** A retain cycle is a non-empty list of paths. It also contains a pointer to the head of the list to model the cycle structure. The next element from the end of the list is the head. *) diff --git a/infer/tests/codetoanalyze/objc/errors/Makefile b/infer/tests/codetoanalyze/objc/errors/Makefile index e21f6312f..30147d9ec 100644 --- a/infer/tests/codetoanalyze/objc/errors/Makefile +++ b/infer/tests/codetoanalyze/objc/errors/Makefile @@ -96,6 +96,7 @@ SOURCES_ARC = \ memory_leaks_benchmark/RetainCycleBlocks.m \ memory_leaks_benchmark/RetainCyclePropertyInProtocol.m \ memory_leaks_benchmark/RetainCycleBlockCapturedVar.m \ + memory_leaks_benchmark/RetainCycleDeduplication.m \ npe/BoxedNumberExample.m \ npe/ObjCMethodCallInCondition.m \ npe/UpdateDict.m \ diff --git a/infer/tests/codetoanalyze/objc/errors/issues.exp b/infer/tests/codetoanalyze/objc/errors/issues.exp index db6dd62aa..acb023d5e 100644 --- a/infer/tests/codetoanalyze/objc/errors/issues.exp +++ b/infer/tests/codetoanalyze/objc/errors/issues.exp @@ -33,6 +33,7 @@ codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleBlockCapturedVar.m, codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleBlocks.m, RCBlock_retain_self_in_block, 1, RETAIN_CYCLE, ERROR, [start of procedure retain_self_in_block,Executing synthesized setter setHandler:] codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleBlocks.m, objc_blockretain_a_in_block_cycle_3, 1, RETAIN_CYCLE, ERROR, [start of procedure block,Executing synthesized setter setChild:] codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleBlocks.m, retain_a_in_block_cycle, 4, RETAIN_CYCLE, ERROR, [start of procedure retain_a_in_block_cycle(),Executing synthesized setter setB:,Executing synthesized setter setA:] +codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleDeduplication.m, CViewController_setCaptureInteractionController:, 4, RETAIN_CYCLE, ERROR, [start of procedure setCaptureInteractionController:,Condition is true,Executing synthesized setter setDelegate:,Executing synthesized setter setDelegate:] codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCyclePropertyInProtocol.m, MyCustomViewController_loadViewBad, 3, RETAIN_CYCLE, ERROR, [start of procedure loadViewBad,Executing synthesized setter setView:,Executing synthesized setter setStrong_delegate:] codetoanalyze/objc/errors/memory_leaks_benchmark/retain_cycle.m, strongcycle, 6, RETAIN_CYCLE, ERROR, [start of procedure strongcycle(),Executing synthesized setter setB:,Executing synthesized setter setA:] codetoanalyze/objc/errors/memory_leaks_benchmark/retain_cycle2.m, strongcycle2, 4, RETAIN_CYCLE, ERROR, [start of procedure strongcycle2(),start of procedure init,return from a call to Parent_init,start of procedure init,return from a call to Child_init,start of procedure setChild:,return from a call to Parent_setChild:,start of procedure setParent:,return from a call to Child_setParent:] diff --git a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleDeduplication.m b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleDeduplication.m new file mode 100644 index 000000000..c9a03cac2 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/RetainCycleDeduplication.m @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#import + +@interface CaptureController : NSObject + +@property(nonatomic, strong, readwrite) id delegate; + +@end + +@implementation CaptureController + +@end + +@interface CViewController : NSObject + +@property(nonatomic, strong) CaptureController* captureController; + +@end + +@implementation CViewController + +- (void)setCaptureInteractionController:(CaptureController*)captureController { + if (_captureController != captureController) { + _captureController.delegate = nil; + _captureController = captureController; + _captureController.delegate = self; + } +} + +@end