From a9e2322bda6e9e5303b08d064005eddd4b1b68fe Mon Sep 17 00:00:00 2001
From: Sungkeun Cho <scho@fb.com>
Date: Mon, 28 Sep 2020 06:10:09 -0700
Subject: [PATCH] [closure] Keep closure parameter in specialized procedure

Summary:
This diff keeps closure parameters in closure-specializated procedures.

What the closure-specialization is doing is a propagation of concrete closures.  For example, it
translates:

```
foo(block b) {
  b();
}

goo() {
  foo(^{...});
}
```

to

```
foo_new() {
  (^{...})();
}

goo() {
  foo_new();
}
```

However, if `foo` addresses `b` as a normal value like

```
foo(block b) {
  block c = b;
}
```

this is translated to

```
foo_new() {
  block c = b;
}
```

Note that the closure parameter of `foo` is removed, thus `b` becomes a free variable.  Not good.
To avoid the situation, this diff keeps the closure parameters intact.

Reviewed By: da319

Differential Revision: D23905580

fbshipit-source-id: 014989fbf
---
 infer/src/clang/CCallSpecializedWithClosures.ml           | 1 +
 infer/src/cost/costAutoreleaseModels.ml                   | 3 ++-
 .../codetoanalyze/objc/frontend/block/retain_cycle.m.dot  | 2 +-
 .../block/specialized_method_with_block_params.m.dot      | 8 ++++----
 .../objc/shared/block/Blocks_as_parameters.m.dot          | 2 +-
 .../tests/codetoanalyze/objc/shared/block/block-it.m.dot  | 2 +-
 6 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/infer/src/clang/CCallSpecializedWithClosures.ml b/infer/src/clang/CCallSpecializedWithClosures.ml
index d510ccbe8..9935f1e49 100644
--- a/infer/src/clang/CCallSpecializedWithClosures.ml
+++ b/infer/src/clang/CCallSpecializedWithClosures.ml
@@ -56,6 +56,7 @@ let formals_actuals_new_set map =
     (fun formal actual set ->
       match actual with
       | Exp.Closure closure, _ ->
+          let set = FormalsActualsSet.add {formal; actual} set in
           List.fold_left closure.Exp.captured_vars ~init:set ~f:(fun set (exp, var, typ, _) ->
               let formal_annot =
                 {formal_type= (Pvar.build_formal_from_pvar var, typ); formal_annot= Annot.Item.empty}
diff --git a/infer/src/cost/costAutoreleaseModels.ml b/infer/src/cost/costAutoreleaseModels.ml
index d84a4778b..f8b147949 100644
--- a/infer/src/cost/costAutoreleaseModels.ml
+++ b/infer/src/cost/costAutoreleaseModels.ml
@@ -50,7 +50,8 @@ module Call = struct
       [ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> unit_cost
       ; -"CFAutorelease" &--> unit_cost
       ; +PatternMatch.ObjectiveC.implements "NSArray"
-        &:: "indexOfObjectPassingTest:" $ capt_exp $--> NSArray.index_of_object_passing_test
+        &:: "indexOfObjectPassingTest:" $ capt_exp $+ any_arg
+        $--> NSArray.index_of_object_passing_test
       ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
         &:: "initForReadingFromData:error:" &--> unit_cost
       ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
diff --git a/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot b/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot
index 416119e31..ca5e9f930 100644
--- a/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot
+++ b/infer/tests/codetoanalyze/objc/frontend/block/retain_cycle.m.dot
@@ -52,7 +52,7 @@ digraph cfg {
 "capture#A#instance.d411336575e4bf632a1828f5f5979726_2" [label="2: Exit A.capture \n  " color=yellow style=filled]
 	
 
-"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3:  Message Call: sHandler: \n   n$3=*&self:A* [line 45, column 4]\n  n$4=*n$3._b:B* [line 45, column 4]\n  n$0=*&self:A* [line 45, column 16]\n  n$5=_fun_B.sHandler:[objc_blockA.capture_1](n$0:A*,n$4:B*) virtual [line 45, column 3]\n " shape="box"]
+"capture#A#instance.d411336575e4bf632a1828f5f5979726_3" [label="3:  Message Call: sHandler: \n   n$3=*&self:A* [line 45, column 4]\n  n$4=*n$3._b:B* [line 45, column 4]\n  n$0=*&self:A* [line 45, column 16]\n  n$5=_fun_B.sHandler:[objc_blockA.capture_1](n$0:A*,n$4:B*,(_fun_objc_blockA.capture_1,([by ref]n$0 &self:A*)):_fn_(*)) virtual [line 45, column 3]\n " shape="box"]
 	
 
 	 "capture#A#instance.d411336575e4bf632a1828f5f5979726_3" -> "capture#A#instance.d411336575e4bf632a1828f5f5979726_2" ;
diff --git a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot
index e864236d0..e32936ab2 100644
--- a/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot
+++ b/infer/tests/codetoanalyze/objc/frontend/block/specialized_method_with_block_params.m.dot
@@ -88,7 +88,7 @@ digraph cfg {
 	
 
 	 "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_3" -> "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_2" ;
-"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" [label="4:  Call _fun_foo \n   n$14=*&self:A* [line 40, column 7]\n  n$16=*&self:A* [line 43, column 7]\n  n$18=*&self:A* [line 46, column 7]\n  n$19=_fun_foo[objc_blockA.bar2_3^objc_blockA.bar2_4](n$14:A*,n$18:A*) [line 39, column 3]\n " shape="box"]
+"bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" [label="4:  Call _fun_foo \n   n$14=*&self:A* [line 40, column 7]\n  n$16=*&self:A* [line 43, column 7]\n  n$18=*&self:A* [line 46, column 7]\n  n$19=_fun_foo[objc_blockA.bar2_3^objc_blockA.bar2_4](n$14:A*,(_fun_objc_blockA.bar2_4,([by ref]n$16 &self:A*)):_fn_(*),(_fun_objc_blockA.bar2_3,([by ref]n$14 &self:A*)):_fn_(*),n$18:A*) [line 39, column 3]\n " shape="box"]
 	
 
 	 "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_4" -> "bar2#A#instance.413fa5106d6a23f2bf18df99659efb82_3" ;
@@ -107,7 +107,7 @@ digraph cfg {
 	
 
 	 "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_4" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_3" ;
-"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" [label="5:  Call _fun_foo \n   n$3=*&self:A* [line 27, column 7]\n  n$4=*&x:int [line 27, column 7]\n  n$7=*&self:A* [line 30, column 7]\n  n$10=*&a:A* [line 33, column 7]\n  n$11=_fun_foo[objc_blockA.bar:_1^objc_blockA.bar:_2](n$4:int,n$3:A*,n$10:A*) [line 26, column 3]\n " shape="box"]
+"bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" [label="5:  Call _fun_foo \n   n$3=*&self:A* [line 27, column 7]\n  n$4=*&x:int [line 27, column 7]\n  n$7=*&self:A* [line 30, column 7]\n  n$10=*&a:A* [line 33, column 7]\n  n$11=_fun_foo[objc_blockA.bar:_1^objc_blockA.bar:_2](n$4:int,n$3:A*,(_fun_objc_blockA.bar:_2,([by ref]n$7 &self:A*)):_fn_(*),(_fun_objc_blockA.bar:_1,([by ref]n$3 &self:A*),([by ref]n$4 &x:int)):_fn_(*),n$10:A*) [line 26, column 3]\n " shape="box"]
 	
 
 	 "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_5" -> "bar:#A(class A)#instance.3e4a860660eb436d473f8ceeb9c1a72b_4" ;
@@ -126,11 +126,11 @@ digraph cfg {
 	
 
 	 "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_3" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_2" ;
-"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" [label="4:  Call _fun_foo \n   n$22=*&b:_fn_(*) [line 55, column 7]\n  n$23=*&b:_fn_(*) [line 55, column 10]\n  n$24=*&self:A* [line 55, column 13]\n  n$25=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,n$24:A*) [line 55, column 3]\n " shape="box"]
+"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" [label="4:  Call _fun_foo \n   n$22=*&b:_fn_(*) [line 55, column 7]\n  n$23=*&b:_fn_(*) [line 55, column 10]\n  n$24=*&self:A* [line 55, column 13]\n  n$25=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,(_fun_objc_blockA.call_foo_with_same_param_5,([by ref]n$30 &self:A*)):_fn_(*),(_fun_objc_blockA.call_foo_with_same_param_5,([by ref]n$30 &self:A*)):_fn_(*),n$24:A*) [line 55, column 3]\n " shape="box"]
 	
 
 	 "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_3" ;
-"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" [label="5:  Call _fun_foo \n   n$26=*&b:_fn_(*) [line 54, column 7]\n  n$27=*&b:_fn_(*) [line 54, column 10]\n  n$28=*&self:A* [line 54, column 13]\n  n$29=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,n$28:A*) [line 54, column 3]\n " shape="box"]
+"call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" [label="5:  Call _fun_foo \n   n$26=*&b:_fn_(*) [line 54, column 7]\n  n$27=*&b:_fn_(*) [line 54, column 10]\n  n$28=*&self:A* [line 54, column 13]\n  n$29=_fun_foo[objc_blockA.call_foo_with_same_param_5^objc_blockA.call_foo_with_same_param_5](n$30:A*,(_fun_objc_blockA.call_foo_with_same_param_5,([by ref]n$30 &self:A*)):_fn_(*),(_fun_objc_blockA.call_foo_with_same_param_5,([by ref]n$30 &self:A*)):_fn_(*),n$28:A*) [line 54, column 3]\n " shape="box"]
 	
 
 	 "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_5" -> "call_foo_with_same_param#A#instance.7f01b1476c1662f7ce825ec7b1739a00_4" ;
diff --git a/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot b/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot
index 6a44c6d59..319ca6961 100644
--- a/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot
+++ b/infer/tests/codetoanalyze/objc/shared/block/Blocks_as_parameters.m.dot
@@ -33,7 +33,7 @@ digraph cfg {
 	
 
 	 "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_2" ;
-"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4:  Message Call: foo:and: \n   n$4=*&self:B* [line 23, column 10]\n  n$5=*n$4.h:int [line 23, column 10]\n  n$2=*&self:B* const  [line 24, column 11]\n  n$6=_fun_B.foo:and:[objc_blockB.f_1](n$5:int,n$2:B* const ) [line 23, column 3]\n " shape="box"]
+"f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" [label="4:  Message Call: foo:and: \n   n$4=*&self:B* [line 23, column 10]\n  n$5=*n$4.h:int [line 23, column 10]\n  n$2=*&self:B* const  [line 24, column 11]\n  n$6=_fun_B.foo:and:[objc_blockB.f_1](n$5:int,n$2:B* const ,(_fun_objc_blockB.f_1,([by ref]n$2 &self:B* const )):_fn_(*)) [line 23, column 3]\n " shape="box"]
 	
 
 	 "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_4" -> "f#B#instance.f1371ff5e7f410d3df6a2e71ff0a814e_3" ;
diff --git a/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot b/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot
index 68f4d5733..3f6bc9e29 100644
--- a/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot
+++ b/infer/tests/codetoanalyze/objc/shared/block/block-it.m.dot
@@ -63,7 +63,7 @@ digraph cfg {
 "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" [label="2: Exit MyBlock.array \n  " color=yellow style=filled]
 	
 
-"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3:  Message Call: enumerateObjectsUsingBlock: \n   n$5=*&a:NSArray* [line 19, column 4]\n  n$6=_fun_NSArray.enumerateObjectsUsingBlock:[objc_blockMyBlock.array_1](n$5:NSArray*) virtual [line 19, column 3]\n " shape="box"]
+"array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" [label="3:  Message Call: enumerateObjectsUsingBlock: \n   n$5=*&a:NSArray* [line 19, column 4]\n  n$6=_fun_NSArray.enumerateObjectsUsingBlock:[objc_blockMyBlock.array_1](n$5:NSArray*,(_fun_objc_blockMyBlock.array_1):_fn_(*)) virtual [line 19, column 3]\n " shape="box"]
 	
 
 	 "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_3" -> "array#MyBlock#instance.8be6e5b5e968d186440e1931c9eb40de_2" ;