Skip to content

Commit 8b18019

Browse files
Chaoqiang Dengfacebook-github-bot
authored andcommitted
Distinguish Singleton and Caching pattern
Summary: This diff distinguishes Caching pattern from Singleton pattern. Essentially, both patterns writes to (or say, initializes) a global variable when its value is null or does not exist. Yet, it is desired to distinguish if the written global variable is a collection or not: if the assignment directly writes to a collection (e.g. `Foo::$dic[$key] = $value`), then we assume it's a caching; otherwise, it is a singleton (e.g. `Foo::$bar = $x` or `(Foo::$dic[$key])->prop = $y`). Reviewed By: lavenzg Differential Revision: D40872670 fbshipit-source-id: 0ec2e66767ef3d1fe241749e4de9607c88222a3e
1 parent a2bdbd6 commit 8b18019

5 files changed

Lines changed: 21 additions & 12 deletions

File tree

hphp/hack/src/typing/tast_check/global_access_check.ml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ module GlobalAccessCheck = Error_codes.GlobalAccessCheck
4343
(* Recognize common patterns for global access (only writes for now). *)
4444
type global_access_pattern =
4545
| Singleton (* Write to a global variable whose value is null *)
46+
| Caching (* Write to a global collection when the element is null or does not exist *)
4647
| CounterIncrement (* Increase a global variable by 1 or -1 *)
4748
| WriteEmptyOrNull (* Assign null or empty string to a global variable *)
4849
| WriteLiteral (* Assign a literal to a global variable *)
@@ -851,17 +852,25 @@ let visitor =
851852
let () = self#on_expr (env, (ctx, fun_name)) re in
852853
let re_ty = Tast_env.print_ty env (Tast.get_type re) in
853854
let le_global_opt = get_global_vars_from_expr env ctx le in
855+
(* When write to a global variable whose value is null or does not exist:
856+
if the written variable is in a collection (e.g. $global[$key] = $val),
857+
then it's asumed to be caching; otherwise, it's a singleton. *)
858+
let singleton_or_caching =
859+
match le with
860+
| (_, _, Array_get _) -> Caching
861+
| _ -> Singleton
862+
in
854863
let le_pattern =
855864
match (le_global_opt, bop_opt) with
856865
| (None, _) -> NoPattern
857-
| (Some _, Some Ast_defs.QuestionQuestion) -> Singleton
866+
| (Some _, Some Ast_defs.QuestionQuestion) -> singleton_or_caching
858867
| (Some le_global, _) ->
859868
if
860869
SSet.exists
861870
(fun v -> SSet.mem v !(ctx.null_global_var_set))
862871
le_global
863872
then
864-
Singleton
873+
singleton_or_caching
865874
else
866875
NoPattern
867876
in

hphp/hack/test/typecheck/global_access_check/test_pattern.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ public function test_method_call(): void {
4545
Foo::$static_dict = HH\Lib\Dict\merge(Foo::$static_dict, dict["0" => 0]); // WriteLiteral,WriteGlobalToGlobal
4646
Foo::$static_dict = HH\Lib\Dict\merge(Foo::$static_dict, dict["0" => (int)call_mixed(0)]); // NoPattern (call_mixed is unknown)
4747
Foo::$static_dict = dict[]; // WriteEmptyOrNUll
48-
Foo::$static_dict["1"] ??= 1; // Singleton
48+
Foo::$static_dict["1"] ??= 1; // Caching
4949
}
5050
}

hphp/hack/test/typecheck/global_access_check/test_pattern.php.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ File "test_pattern.php", line 46, characters 43-59:
4747
File "test_pattern.php", line 47, characters 5-30:
4848
[Test::test_method_call]{Foo::$static_dict}(dict<nothing, nothing>)<WriteEmptyOrNull> A global variable is definitely written. (Other[11001])
4949
File "test_pattern.php", line 48, characters 5-32:
50-
[Test::test_method_call]{Foo::$static_dict}(int)<Singleton,WriteLiteral> A global variable is definitely written. (Other[11001])
50+
[Test::test_method_call]{Foo::$static_dict}(int)<Caching,WriteLiteral> A global variable is definitely written. (Other[11001])

hphp/hack/test/typecheck/global_access_check/test_singleton_write_collection.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ public static function get_dict_value(string $key): Bar {
2525
$value = idx(self::$static_dic, $key);
2626
if ($value === null) {
2727
$value = new Bar();
28-
self::$static_dic[$key] = $value; // Singleton
28+
self::$static_dic[$key] = $value; // Caching
2929
}
3030
return $value;
3131
}
3232

3333
public static function get_dict_value2(string $key): Bar {
3434
if (!HH\Lib\C\contains_key(self::$static_dic, $key)) {
35-
self::$static_dic[$key] = new Bar(); // Singleton
35+
self::$static_dic[$key] = new Bar(); // Caching
3636
}
3737
return self::$static_dic[$key];
3838
}
@@ -41,7 +41,7 @@ public static function get_shape_value(): Bar {
4141
$value = self::$static_shape["field1"];
4242
if ($value === null) {
4343
$value = new Bar();
44-
self::$static_shape["field1"] = $value; // Singleton
44+
self::$static_shape["field1"] = $value; // Caching
4545
}
4646
return $value;
4747
}
@@ -50,7 +50,7 @@ public static function get_dict_value_3(string $key): Bar {
5050
$value = arbitrary_func(self::$static_dic, $key);
5151
if ($value === null) {
5252
$value = new Bar();
53-
self::$static_dic[$key] = $value; // Not a singleton
53+
self::$static_dic[$key] = $value; // Not caching
5454
}
5555
return $value;
5656
}
@@ -59,7 +59,7 @@ public static function get_dict_value_4(string $key): Bar {
5959
$exist_key = HH\Lib\C\contains_key(self::$static_dic, $key);
6060

6161
if (!$exist_key) {
62-
self::$static_dic[$key] = new Bar(); // FN: singleton not recognized
62+
self::$static_dic[$key] = new Bar(); // FN: caching not recognized
6363
}
6464
return self::$static_dic[$key];
6565
}

hphp/hack/test/typecheck/global_access_check/test_singleton_write_collection.php.exp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ File "test_singleton_write_collection.php", line 25, characters 18-34:
33
File "test_singleton_write_collection.php", line 25, characters 18-34:
44
[Foo::get_dict_value]{Foo::$static_dic}(dict<string, Bar>) A global variable is possibly written via function call. (Other[11003])
55
File "test_singleton_write_collection.php", line 28, characters 7-38:
6-
[Foo::get_dict_value]{Foo::$static_dic}(Bar)<Singleton> A global variable is definitely written. (Other[11001])
6+
[Foo::get_dict_value]{Foo::$static_dic}(Bar)<Caching> A global variable is definitely written. (Other[11001])
77
File "test_singleton_write_collection.php", line 30, characters 12-17:
88
[Foo::get_dict_value]{Foo::$static_dic}(Bar) A global variable is possibly written via function call. (Other[11003])
99
File "test_singleton_write_collection.php", line 34, characters 32-48:
1010
[Foo::get_dict_value2]{Foo::$static_dic}(dict<string, Bar>) A global variable is definitely read. (Other[11004])
1111
File "test_singleton_write_collection.php", line 34, characters 32-48:
1212
[Foo::get_dict_value2]{Foo::$static_dic}(dict<string, Bar>) A global variable is possibly written via function call. (Other[11003])
1313
File "test_singleton_write_collection.php", line 35, characters 7-41:
14-
[Foo::get_dict_value2]{Foo::$static_dic}(Bar)<Singleton> A global variable is definitely written. (Other[11001])
14+
[Foo::get_dict_value2]{Foo::$static_dic}(Bar)<Caching> A global variable is definitely written. (Other[11001])
1515
File "test_singleton_write_collection.php", line 37, characters 12-28:
1616
[Foo::get_dict_value2]{Foo::$static_dic}(dict<string, Bar>) A global variable is definitely read. (Other[11004])
1717
File "test_singleton_write_collection.php", line 37, characters 12-34:
1818
[Foo::get_dict_value2]{Foo::$static_dic}(Bar) A global variable is possibly written via function call. (Other[11003])
1919
File "test_singleton_write_collection.php", line 41, characters 14-32:
2020
[Foo::get_shape_value]{Foo::$static_shape}(shape('field1' => ?Bar, 'field2' => ?Bar)) A global variable is definitely read. (Other[11004])
2121
File "test_singleton_write_collection.php", line 44, characters 7-44:
22-
[Foo::get_shape_value]{Foo::$static_shape}(Bar)<Singleton> A global variable is definitely written. (Other[11001])
22+
[Foo::get_shape_value]{Foo::$static_shape}(Bar)<Caching> A global variable is definitely written. (Other[11001])
2323
File "test_singleton_write_collection.php", line 46, characters 12-17:
2424
[Foo::get_shape_value]{Foo::$static_shape}(Bar) A global variable is possibly written via function call. (Other[11003])
2525
File "test_singleton_write_collection.php", line 50, characters 29-45:

0 commit comments

Comments
 (0)